PDO 准备状态与手动查询的结果不同

Posted

技术标签:

【中文标题】PDO 准备状态与手动查询的结果不同【英文标题】:PDO prepared state not giving same result as manual query 【发布时间】:2015-02-24 11:27:36 【问题描述】:

我正在使用 php 和 PDO 准备好的语句来访问数据库。我有这个准备好的查询,我想执行(通过一个 foreach 循环多次,但我看不出这真的会影响这个):

insert into forum_access (forum_id, user_id) select * from (select ?, ?)
 as tmp where not exists (select * from forum_access 
 where forum_id = ? and user_id = ?) limit 1

然后我使用 PDO 执行与数组中的变量来执行此语句,如下所示:

$values = Array(2, 1, 2, 1); // Normally it's variables here
$stmt->execute($values);

这会执行,但奇怪的是它会在 forum_access 中插入一个值为 (2, 2) 的行。真正奇怪的是,当我使用手动插入的变量运行 SQL 查询时,如下所示:

insert into forum_access (forum_id, user_id) select * from (select 2, 1)
 as tmp where not exists (select * from forum_access 
 where forum_id = 2 and user_id = 1) limit 1

它正确地插入了具有值 (2, 1) 的行。

我希望这与 PDO/mysql 处理预准备语句的方式有关。我或多或少是准备好的陈述的新手,不知道这里出了什么问题。希望其他人可以对此有所了解。

注意事项: 我有理由在重复键上使用相当复杂的插入...选择查询而不是插入...。也许不是完美的理由,但足以对从根本上改变查询的建议不感兴趣。

在 WAMP 服务器上使用 PHP 5.3 和 MySQL 5.0。

【问题讨论】:

你能导出准备好的语句本身吗? 你检查参数的顺序了吗?可能是有什么问题。命名参数会更好 【参考方案1】:

我自己解决了这个问题。似乎 MySQL 对 select 语句中的插入值感到困惑,并将它们混合在一起。我试图命名这些值,如下所示:

insert into forum_access (forum_id, user_id) select * from 
 (select ? as forum_id, ? as user_id) as tmp 
 where not exists (select * from forum_access 
 where forum_id = ? and user_id = ?) limit 1

你瞧,这确实有效。

我仍然不确定它为什么会起作用。但我最关心的是获得一个可以工作的应用程序,所以目前我很满足。

感谢您的所有帮助,很抱歉打扰了我可以并且确实解决了自己的问题。

【讨论】:

【参考方案2】:
$SQL = 'insert into forum_access (forum_id, user_id) 
        select * 
        from (select :forum_id1, :user_id1) as tmp 
        where not exists (
             select * 
             from forum_access 
             where forum_id = :forum_id2 and user_id = :user_id2) 
        limit 1';

$forum_id1 = 2;
$user_id1  = 1;
$forum_id2 = 2;
$user_id2  = 1;

$stmt = $dbh->prepare($SQL);

$stmt->bindParam(':forum_id1', $forum_id1, PDO::PARAM_INT);
$stmt->bindParam(':user_id1',  $user_id1,  PDO::PARAM_INT);
$stmt->bindParam(':forum_id2', $forum_id2, PDO::PARAM_INT);
$stmt->bindParam(':user_id2',  $user_id2,  PDO::PARAM_INT);

$stmt->execute();

【讨论】:

您的建议是切换到命名占位符?您能否详细说明这将如何产生影响? 不幸的是,无论是这个解决方案还是 Peter Darmis 提出的解决方案似乎都不能解决问题。我还尝试将我的应用程序导出到 LAMP 服务器(PHP:5.4.4,MySQL:5.5.38),这导致错误 1060:列名重复“?”。我不确定如何正确解释这一点,但它似乎在某种程度上与 WAMP 服务器上奇怪的插入值有关。双 ?在插入中在某种程度上在准备好的语句解析中混乱,这在 LAMP 上给出了错误,在 WAMP 上给出了奇怪的插入值。不过还是不知道怎么解决。 请使用 'bindValue' 而不是 'bindParam' 因为您可以使用 'calculated' 值。仅对“blob”等“大型对象”使用“bindParam”。如果您使用 'bindParam',那么您必须传递一个包含所需值的 PHP 变量。自 PHP 5.3 以来,这种情况发生了变化吗? 这还能用吗? $stmt->bindParam(':forum_id1', 2, PDO::PARAM_INT) 什么时候运行?我希望它会引发错误,因为您没有将 PHP 变量传递给“绑定”。请告诉我它适用于哪个版本的 PHP。【参考方案3】:

改变这个

$values = Array(2, 1, 2, 1); // Normally it's variables here
$stmt->execute($values);

有了这个

$values = Array(2, 1, 2, 1);
for($i=0;$i<count($values);$i++) 
$stmt->bindParam(($i+1),$values[$i]); 

$stmt->execute();

【讨论】:

这只会使代码更加冗长。我不明白为什么它会有所作为:-? 除非必须,否则不要使用'bindParam'。除非您传递一些“大”的东西,例如“blob”,否则请使用“bindValue”。

以上是关于PDO 准备状态与手动查询的结果不同的主要内容,如果未能解决你的问题,请参考以下文章

PDO 在表格中显示结果

PDO 准备和执行查询总是返回错误

mysql pdo查询结果 int 变成结果string 解决

PDO 在 while 循环内运行查询,仅显示 1 个结果

将 PDO 与 MS SQL 结合使用“活动结果不包含任何字段”

mysql性能中的PDO