【问题标题】:PDO::bindValue() fails on query with nested SELECTPDO::bindValue() 使用嵌套 SELECT 查询失败
【发布时间】:2017-08-29 15:27:50
【问题描述】:

我正在使用通过 ODBC 连接的 MSSQL 数据库。

在具有嵌套 SELECT 语句的查询上使用 PDO::bindValue() 时,它无法在嵌套 SELECT 中绑定值(在主 SELECT 上没有问题)。 这是一段失败的示例代码:

$stmt = $cmdb->prepare("SELECT ci.CI FROM dbo.cmdb_ci AS ci " .
                       "INNER JOIN dbo.cmdb_model AS m ON m.ModelID = ci.Modelid " .
                       "INNER JOIN dbo.cmdb_class AS c ON c.ClassID = m.Classid " .
                       "WHERE (c.ClassID = :classid) " .
                       "AND (ci.CI IN (SELECT ci2.CI " .
                                      "FROM dbo.cmdb_ci AS ci2 " .
                                      "INNER JOIN dbo.cmdb_ci_status AS st2 ON st2.CI = ci2.CI " .
                                      "WHERE st2.LocationID = :locationid))");
$stmt->bindValue("classid", 13);
$stmt->bindValue("locationid", 1011);
$stmt->execute();
if ($rows = $stmt->fetchAll())
    $stmt->closeCursor();
foreach ($rows as $row)
    echo $row["CI"];

我得到的错误是:

SQLSTATE[22018]:强制转换规范的字符值无效:206 [Microsoft][SQL Server Native Client 11.0][SQL Server]操作数类型冲突:文本与 int 不兼容(SQLExecute[206] 在 /builddir/build/ BUILD/php-5.4.16/ext/pdo_odbc/odbc_stmt.c:254)

如果我为“:locationid”省略bindValue() 并直接在查询中插入“1011”,则调用完成且没有错误且结果正确。

这是 PDO 中的错误,还是我必须以不同的方式调用 bindValue()?

【问题讨论】:

  • 您的数据库中的 LocationID 是否设置为 int 或 text?
  • $stmt->bindValue("locationid", 1011,PDO::PARAM_INT); 怎么样,正如错误所说,你用文本转换 int 。没有 PDO:PARAM_ 的 bindValue 变成一个字符串,例如文本。
  • LocationID 被定义为一个整数。
  • 不是$stmt->bindValue("classid", 13); 而是$stmt->bindValue(":classid", 13);
  • stackoverflow.com/questions/16532694/… 有疑问,我会迁移到最新的稳定版 php

标签: php pdo


【解决方案1】:

正如(我从 cmets 读到的)告诉 bindValue 传递的值是一个整数没有解决问题...

$stmt->bindValue( "locationid", 1011, PDO::PARAM_INT );

...我假设出于某种原因 (a bug in pdo_odbc ?),无论您在 bindValue 上指定什么作为第三个参数,参数都会作为字符串输入查询。

然后我建议通过将值转换为整数就地到查询中来解决这个问题。

在最后一行:

"WHERE st2.LocationID = CAST( :locationid, int ) ))"

这不是很优雅,但在您找到 pdo_odbc 的修复/补丁之前可能是合适的


如果这不起作用还有一个更不优雅的解决方案(当然是作为临时解决方案)。

你写道:

如果我省略 ":locationid" 的 bindValue() 并将 '1011' 直接插入到查询中,则调用将完成且没有错误且结果正确。

因此您可以直接将 location id 放入查询中。

假设 location id 存储在$locationId 中,那么查询的最后一行变为:

"WHERE st2.LocationID = $locationId))");

由于这很容易发生 sql-injection $locationId 必须事先清理(或验证)。

该值必须是一个正整数,因此我建议使用更简单且防弹的方法,而不是转义它:检查 $locationId 仅由数字组成...

if( ! ctype_digit( (string) $locationId ) ) {
    // location id is invalid
    // do not proceed !
}

【讨论】:

    【解决方案2】:

    会不会是 Location ID 实际上是作为字符串而不是 int 存储在 MSSQL 中的?添加引号时是否有效? $stmt->bindValue("locationid", "1011", PDO::PARAM_STR);

    【讨论】:

    • 不,这不起作用,因为 LocationID 值被定义为 INT(见上文),因此这将使其成为无效的 SQL 语句。
    猜你喜欢
    • 2014-01-26
    • 2018-10-04
    • 2015-10-08
    • 2014-05-08
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    相关资源
    最近更新 更多