0

I am doing 3 queries to get various data in my script. It runs fine, but can probably be done more efficiently using one query. I fine with doing basic one table queries, but am not sure how to even start doing what I am doing below. I want to retrieve all the fields from the payments table, and then the requests table where the shortcode is the same as the payment tables shortcode and then user information where the user matches the user in the requests table. I think I need to use joins or something, but am not sure how to structure this. Thanks in advance for any help.

$emls = str_replace(';',',',$clientinfo[0]['PaymentNotificationEmail']);
$stmt = $conn->prepare('SELECT * FROM payments WHERE id = :id');
$stmt->bindValue(':id',$id);
$stmt->execute();
if ($stmt->rowCount() == 1){
 $prec = $stmt->fetchAll(PDO::FETCH_ASSOC);
 $shortcode = $prec[0]['shortcode'];
 if ($shortcode != ''){
  $stmt = $conn->prepare('SELECT * FROM payrequests WHERE id = :id');
  $stmt->bindValue(':id',$shortcode);
  $stmt->execute();
  if ($stmt->rowCount() == 1){
   $rrec = $stmt->fetchAll(PDO::FETCH_ASSOC);
   $uid = $rrec[0]['user'];
   if ($uid != ''){
    $stmt = $conn->prepare('SELECT * FROM clientusers WHERE clientid = :clientid AND id = :id');
    $stmt->bindParam(':id', $uid);
    $stmt->bindParam(':clientid', $clientid);
    $stmt->execute();
    if ($stmt->rowCount() != 1){echo 'User Missing';die;}
    $urec = $stmt->fetchAll(PDO::FETCH_ASSOC);
    if ($urec[0]['email'] != '' && $urec[0]['bccout'] == 'Y'){
     if ($emls != ''){$emls .= ',';}
     $emls .= $urec[0]['email'];
    }
   }
  }
 }
}
user999684
  • 705
  • 2
  • 10
  • 23
  • Is it possible that there is no payrequest or user associated with a given payment as implied by your `if ($shortcode != '')` and `if ($uid != '')` tests? – Nick Jan 22 '21 at 19:59
  • Yes, I forgot to include that. There may not be a shortcode, but should always be a $uid. I added that as an additional check as well just to be safe. – user999684 Jan 22 '21 at 20:02

1 Answers1

1

It looks like the query you are looking for is

SELECT *
FROM payments p
LEFT JOIN payrequests pr ON pr.id = p.shortcode
LEFT JOIN clientusers c ON c.id = pr.user AND c.clientid = :clientid
WHERE p.id = :id

Note that this will give you all the columns from each table, which may cause issues if you have similar column names (e.g. id), as the last value read will overwrite the earlier ones. To avoid that problem, you should list all columns that you want to read separately, and use column aliases as necessary to distinguish from them. For example,

SELECT p.id AS payment_id, p.col1, p.col2,
       pr.id AS payrequest_id, pr.col1, pr.col2,
       c.id AS clientuser_id, c.clientid, c.col2
FROM ...

Note also that for payments with no corresponding payrequest or clientuser, the columns from those tables will be NULL.

Nick
  • 138,499
  • 22
  • 57
  • 95
  • Thanks! I think this is what I need to get going. I am sure this will be faster than what I was doing with multiple calls, correct? I have indexes on the reference columns. – user999684 Jan 22 '21 at 21:23
  • Yes, this will be faster than doing multiple queries. Let me know if you need more info – Nick Jan 22 '21 at 22:03
  • I am getting the error "1267 Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT)" I did some research and am not sure which to choose. I am on aws version 5.7.12. Can I set everything the same? I am using phpma. – user999684 Jan 22 '21 at 23:11
  • I don't understand why you wouldn't have got this error before? Or are the `id` columns in `payrequests` and `clientusers` strings? If so, you really should change your tables to all have the same collation. I would also recommend reading [this q&a](https://stackoverflow.com/questions/279170/utf-8-all-the-way-through) – Nick Jan 22 '21 at 23:48
  • I think because I was only doing one table at a time. I read most of the links and the links to the links. It says to make them all utf8mb4, but my question is, what version? utf8mb4_general_ci, utf8mb4_0900_ai_ci or something else. Looks the utf8mb4_0900_ai_ci is the default going forward, should I set everything to that? It says the general is faster, but less accurate. – user999684 Jan 23 '21 at 01:56
  • I would always go for accuracy over speed; computers etc. are getting faster all the time but you can never fix wrong results. – Nick Jan 23 '21 at 02:04
  • I noticed all the tables are either utf8mb4_general_ci or utf8mb4_0900_ai_ci except one which is latin1_swedish_ci. Is there any risk to changing these all to utf8mb4_0900_ai_ci? – user999684 Jan 23 '21 at 02:21
  • There shouldn't be; having said that you should *always* take backup copies before making changes to entire tables, just in case. – Nick Jan 23 '21 at 02:31
  • Yeah, just took a back and also made a copy of the DB. Thanks for all your help! – user999684 Jan 23 '21 at 02:33
  • @user999684 no worries - I'm glad I could. – Nick Jan 23 '21 at 02:39
  • I tried it and it worked perfectly. I have a question, I did a new select from the payrequest table to the payments table which is a one to many relationship. It duplicates the data from the payrequests table for every matching record from the payments table. Is there a way to make it return the payrequests data once and the payments data as an array of records for each payrequests line? Basically in json representation: [{"id":"1","shortcode":"xyzzy","payments":[{"date":"01/01/21","time":"01:01:12"},{"date":"02/01/21","time":"02:01:12"}]}] – user999684 Jan 23 '21 at 18:53
  • In my research, it appears this cannot be done without using a code to build the data that is returned in the query. – user999684 Jan 23 '21 at 20:23
  • @user999684 if you're running MySQL 5.7.22 or later, you can use [`JSON_ARRAYAGG`](https://dev.mysql.com/doc/refman/5.7/en/aggregate-functions.html#function_json-arrayagg) and [`JSON_OBJECT`](https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html#function_json-object) to achieve that result directly in a query; otherwise it is simpler to build it in the application. – Nick Jan 23 '21 at 22:25