【问题标题】:Executing a prepared PDO statement with the like clause [duplicate]使用like子句执行准备好的PDO语句[重复]
【发布时间】:2011-04-23 00:37:30
【问题描述】:

我是 PHP 新手,正在尝试学习使用 PDO 连接到测试 MySQL 数据库。我有以下内容:

try {
    $db = new PDO('mysql:dbname=MYDBNAME;host=MYHOST', 'USERNAME', 'PASSWORD');

    $query = "select * from books where ? like '%?%'";
    $stmt = $db->prepare($query);
    $stmt->execute(array($searchtype, $searchterm));  
} catch(PDOException $e) {
    echo 'PDOException: ' . $e->getMessage();
}

当我尝试它时,我收到以下警告: 警告:PDOStatement::execute() [pdostatement.execute]: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

当我删除 like 子句和 $searchterm 参数时,它会正确返回结果。我想——比如'%?%'——可能不是在双引号下创建这个查询的合法方式,所以我尝试转义',但没有用。我四处寻找解决方案,发现有人将 '% and %' 移到 $searchterm 所在的位置:

$query = "select * from books where ? like ?";
...
$stmt->execute(array($searchtype, '\'%'.$searchterm.'%\'')); 

我得到了同样的结果。
任何帮助表示赞赏。谢谢!

/ 更新 ****/ 我在http://us3.php.net/manual/en/pdo.prepared-statements.php的示例12中找到了

示例 #12 无效使用占位符

<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute(array($_GET['name']));

// Below is What they suggest is the correct way.
// placeholder must be used in the place of the whole value 
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
?> 

我试过这个,即使我不再收到警告,我也没有得到任何结果。但是,当我直接执行查询时,我会得到几个结果。有什么想法吗?

【问题讨论】:

  • 我遇到了同样的问题,就像你一样,我认为 '%' 是查询结构的一部分而不是值;)

标签: php sql pdo prepared-statement


【解决方案1】:

绑定准备好的变量时不要加引号,不要绑定列名

$query = sprintf( "select * from books where %s like ?", $searchtype );
...
$stmt->execute(array($searchtype, '%'.$searchterm.'%')); 

【讨论】:

  • 我试过了,但是没有用。我认为 bobince 把它放在另一个答案中的问题是它不把 % 作为通配符,而是一个字面的 '%',这不是我的意图。
【解决方案2】:
$stmt->execute(array($searchtype, '\'%'.$searchterm.'%\'')); 

这不是参数化查询的工作方式。插入的参数已经充当文字字符串,您不必在它们周围添加引号分隔符或转义它们(这就是重点),如果您尝试,您实际上是在与字符串 single-quote-searchterm-single- 进行比较引用。

因此,如果您(我怀疑)打算将特定列与文字字符串进行比较,则无需参数化列名。目前,您正在将文字字符串与另一个文字字符串进行比较,因此无论行中的数据如何,它都将始终为真或始终为假!

所以我想你的意思可能是:

$query= "SELECT * FROM books WHERE $searchtype LIKE ?";
$like= "%$searchterm%";
$stmt->execute(array($like)); 

自然认为您必须非常小心 $searchtype 是已知的,以避免 SQL 注入。通常,您会在使用它之前将其与可接受的列名列表进行比较。

(旁白:一种将任意字符串放入可用于列的架构名称的方法,但它很烦人,因数据库而异,并且没有标准的转义函数它。在 MySQL 中,您使用反斜杠转义反引号字符、引号和反斜杠,并用反引号将名称括起来。在 ANSI SQL 中,您使用双引号和内部的双双引号。在 SQL Server 中,您使用方括号。但实际上您很少需要这样做,因为实际上您只想允许一些预定义的列名。)

(另一边:如果您希望能够允许 $searchterm 值带有文字百分比、下划线或反斜杠 - 这样用户就可以在不匹配 any 字符串与 @ 的情况下搜索“100%” 987654325@ in——你必须使用显式的转义字符,这有点乏味:)

$query= "SELECT * FROM books WHERE $searchtype LIKE ? ESCAPE '+'";
$like= str_replace(array('+', '%', '_'), array('++', '+%', '+_'), $searchterm);
$stmt->execute(array("%$like%")); 

【讨论】:

  • 不会是array('++', '+%', '+_')array('\+', '\%', '\\_'),甚至是array('\%', '\\_')(为什么要转义+)? (顺便问一下,为什么这里的 Markdown 会把\\_ 转换成_?)
  • 这行得通!!!但是如何修剪()空间?
【解决方案3】:

我看到的问题是,如果您为 PDO 编写了一个包装器,那么您将不得不以某种方式单独处理它。我找到并喜欢的答案是编写您的查询并将 % 连接到参数。即“WHERE 列,如 concat('%', :something, '%')”

【讨论】:

    猜你喜欢
    • 2013-10-26
    • 1970-01-01
    • 2012-11-29
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多