PHP MySQL Prepared Statement Join 错误

Posted

技术标签:

【中文标题】PHP MySQL Prepared Statement Join 错误【英文标题】:PHP MySQL Prepared Statement Join Error 【发布时间】:2011-10-21 10:10:03 【问题描述】:

更新:请见下文;我已经删除了所有抽象并创建了一个普通的 php 脚本,但仍然得到同样的错误。请参阅下面的“另一个更新”以获取更多信息...

原帖:我有一个通过数据库抽象层正确形成的查询。查询如下:

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

如果我生成相同的查询,但将其作为准备好的语句运行,该语句将 WHERE 子句的第二个操作数参数化,如下所示,我会得到不正确的结果。我确信该语句本身是正确的,并且我正在运行 mysqli prep。语句以正确的顺序运行。准备好的语句查询如下:

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

此查询似乎将参数化的5c9_dpd_users.cms_usergroup_id 转换为简单的“5”,因为运行该查询会从SELECT 子句中检索正确的列,但对于所有结果返回相同的usergroup_name - 此用户组名称匹配具有5c9_dpd_usergroups_cms.id5 的结果的用户组名称。

我正在动态绑定参数 (call_user_func_array())。在本例中,我告诉它将? 参数绑定为i。我想也许这使它出于某种原因将5c9_dpd_users.cms_usergroup_id 转换为5,但我已经尝试了所有其他三种数据类型(sbd),但没有运气。

注意:我多次使用我的数据库抽象层都没有问题,尽管这是它运行连接的第一个实例(它是最近才开发的)。重申一下,如果没有参数化,查询通过 PHP 和 MySQL 控制台返回正确的结果,但如果通过 PHP 准备语句函数给出?,则结果不正确,如上所述。

如果有人能提供帮助,我将不胜感激。我整晚都在谷歌上搜索这件事的帮助,我在任何地方都找不到任何类似的实例。谢谢。

编辑(提供 PHP 代码):

$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();

查询现在已执行,我所要做的就是调用 $stmt->fetch_prep(),这是一个基本上检索结果集的包装方法。此客户端代码生成的查询是上面给出的第二个 - 我作为示例给出的参数化 - 查询。

我在 $stmt->execute_prep() 方法中打印了一些调试代码,绑定到查询的参数正确显示为:

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

这意味着它将一个参数(键 1)绑定为 int(键 0)。我也尝试将它绑定为字符串,double 和 blob,但没有效果。

另一个更新:

我创建了一个测试脚本来完全移除我的数据库抽象层,结果是一样的:没有返回cms用户组名。这是脚本:

<?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);
?>

输出:

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

【问题讨论】:

php.net/manual/en/mysqli-stmt.bind-param.php 那里有一个关于将 call_user_func_array() 与绑定参数结合使用到语句的警告。您认为这可能会影响您吗? 我不这么认为,因为传递非引用参数会导致致命/警告错误(我忘记了,确切地说)。 你能展示一些你用来绑定参数的代码吗?谁知道呢,你可能错过了那里的东西。 我会展示我能做的;然而,这一切都是抽象的,所以恐怕不会完全清楚...... 5 在某种程度上是第一个用户组 ID 吗?好像是设置了一次就没有修改了? 【参考方案1】:

也许我只是有点挑剔,但我不会以数字开头的表/字段名称。使用一个字母或用反引号将这些奇怪的名称括起来 (`)。它应该可以工作。

编辑(根据您的问题更改)

你编辑的脚本的某些地方听起来提醒我:

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

如 php 手册中所述,mysqli::prepare 在 where 子句中接受参数并且不允许 更改查询的列名。

此外,对字符串进行整数绑定应将值转换为数字,然后再将其插入准备好的查询中,并且您的 $params 应转换为 0。

发出sqlstate 语句以检查执行后查询的情况:

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

一个可能的解决方案:

<?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);
?>

【讨论】:

我已经重命名了表名(我的查询也相应地改变了)但是现在,没有检索到用户组名... 我自己多次复制了上述内容,但不确定占位符为什么不起作用;知道我无法更改查询的列名当然可以解释为什么我的尝试会产生意想不到的结果!为了解决与我的 DB 抽象层相关的问题,我添加了另一个参数,该参数必须由客户端代码在所有 WHERE 和 ON 子句中提供,用于说明参数是否要占位符!非常感谢您的澄清!

以上是关于PHP MySQL Prepared Statement Join 错误的主要内容,如果未能解决你的问题,请参考以下文章

PHP PDO - 关闭语句 - 不能创建超过 max_prepared_stmt_count 语句

MySQL Prepared PDO 语句正在删除反斜杠

具有多个连接的 mysqli Prepared 语句不断返回 false [关闭]

PHP MySQLi Prepared Statements Tutorial to Prevent SQL Injection

PHP do/while Prepared Statement 失败

带有子查询的PHP Prepared Statement变量绑定错误