简体   繁体   中英

PHP MySQL Prepared Statement Join Error

Update: Please see below; I have removed all abstraction and created a plain PHP script but still get the same error. Please see 'ANOTHER update' below for more information...

Original Post: I have a query which has been correctly formed through a database abstraction layer. The query is the following:

SELECT 5c9_dpd_users.id, 5c9_dpd_users.username, 5c9_dpd_users.cms_usergroup_id,  
5c9_dpd_usergroups_cms.usergroup_name, 5c9_dpd_users.email FROM 5c9_dpd_users LEFT JOIN
5c9_dpd_usergroups_cms ON 5c9_dpd_usergroups_cms.id = 5c9_dpd_users.cms_usergroup_id 
ORDER BY username asc

If I generate the same query but run it as a prepared statement which parameterises the second operand of the WHERE clause as below, I get incorrect results. I am certain that the statement itself is correct and that I am running the MySQLi prep. statement functions in the right order. The prepared statement query is below:

SELECT 5c9_dpd_users.id, 5c9_dpd_users.username, 5c9_dpd_users.cms_usergroup_id, 
5c9_dpd_usergroups_cms.usergroup_name, 5c9_dpd_users.email FROM 5c9_dpd_users LEFT JOIN 
5c9_dpd_usergroups_cms ON 5c9_dpd_usergroups_cms.id = ? ORDER BY username asc

This query seems to convert the 5c9_dpd_users.cms_usergroup_id that is parameterized to simply '5', since running the query retrieves the right columns from the SELECT clause but returns the same usergroup_name for all results - this usergroup name matches the usergroup name for the result that has a 5c9_dpd_usergroups_cms.id of 5 .

I am binding parameters dynamically ( call_user_func_array() ). In this instance, I tell it to bind the ? parameter as an i . I thought perhaps this made it convert the 5c9_dpd_users.cms_usergroup_id to a 5 for some reason, but I have tried all three other data types ( s , b and d ) with no luck.

NB: I have used my database abstraction layer without problem many times, although this IS the first instance in which it has run a join (it has only been recently developed). To reiterate, the query returns the correct result through PHP and the MySQL console if it is not parameterized, but if given a ? through the PHP prepared statement functions, the results are incorrect, as described above.

If anyone could help, I would greatly appreciate it. I've been up all night googling for help on this matter, and I can't find any similar instance documented anywhere. Thank you.

Edit (provides PHP code):

$stmt->select_prep(array('users.id', 'users.username', 'users.cms_usergroup_id', 'usergroups_cms.usergroup_name', 'users.email'), array('users'));
$stmt->left_join_prep('usergroups_cms');
$stmt->on_prep('usergroups_cms.id', '=', 'users.cms_usergroup_id', 'i');

$stmt->order($column, $asc_desc);
$stmt->execute_prep();

The query is now executed, and all I have to do is call $stmt->fetch_prep(), a wrapping method that basically retrieves a result set. The query that this client code generates was given above as the second - the parameterized - query I gave as an example.

I printed out some debugging code in my $stmt->execute_prep() method and the parameter that gets bound to the query appears correctly as:

Array
(
  [0] => i
  [1] => 5c9_dpd_users.cms_usergroup_id
)

This means it is binding one parameter (key 1) as an int (key 0). I have also tried binding it as a string, double and blob, to no effect.

ANOTHER Update:

I created a test script to completely remove my database abstraction layer, and the results are the same: no cms usergroup name is returned. Here is the script:

<?php
$mysqli = new mysqli('localhost', 'root', '', 'frame');

$q = "SELECT ac9_dpd_users.id, ac9_dpd_users.username, ac9_dpd_users.cms_usergroup_id, 
ac9_dpd_usergroups_cms.usergroup_name, ac9_dpd_users.email 
FROM ac9_dpd_users 
LEFT JOIN ac9_dpd_usergroups_cms 
ON ac9_dpd_usergroups_cms.id = ? 
ORDER BY username asc";

$stmt = $mysqli->prepare($q);
$stmt->bind_param('i', $param);
$param = 'ac9_dpd_users.cms_usergroup_id';
$stmt->execute();

$stmt->bind_result($a, $b, $c, $d, $e);

while ($stmt->fetch()) {
    echo "id: $a <br>un: $b <br>id: $c <br>cms name: $d <br>email: $e<br><br>";
}

$stmt->close();
unset($mysqli);
?>

Output:

id: 7 
un: ADD 
id: 1 
cms name: 
email: test@test.com

id: 1 
un: New User 
id: 2 
cms name: 
email: test@test.com

etc

Maybe I'm just a bit picky but I wouldn't start a table/field name with a digit. Use a letter or surround these weird names with backtick ( ` ). It should work.

Edit (according to your question changes)

Some spot of your edited script sound alerting to me:

     ...
     ON ac9_dpd_usergroups_cms.id = ? 
     ...
     $stmt->bind_param('i', $param);
     $param = 'ac9_dpd_users.cms_usergroup_id';

As reported into the php manual, mysqli::prepare accepts parameters into where clause and does not permit to change the columns name of the query.

Furthermore doing a integer bind of a string should convert the value to a number before inserting it into the prepared query and your $params should be converted to 0.

Issue a sqlstate statement to check what is going with the query just after your execute:

...
$stmt->execute();
$sqlError = $stmt->sqlstate();
if($sqlError != '00000') die($sqlError);
...

A possible solution:

<?php
$mysqli = new mysqli('localhost', 'root', '', 'frame');

$q = "SELECT ac9_dpd_users.id, ac9_dpd_users.username, ac9_dpd_users.cms_usergroup_id, 
ac9_dpd_usergroups_cms.usergroup_name, ac9_dpd_users.email 
FROM ac9_dpd_users 
LEFT JOIN ac9_dpd_usergroups_cms 
ON ac9_dpd_usergroups_cms.id = ac9_dpd_users.cms_usergroup_id 
ORDER BY username asc";

$stmt = $mysqli->prepare($q);
$stmt->execute();

$stmt->bind_result($a, $b, $c, $d, $e);

while ($stmt->fetch()) {
    echo "id: $a <br>un: $b <br>id: $c <br>cms name: $d <br>email: $e<br><br>";
}

$stmt->close();
unset($mysqli);
?>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM