使用like子句执行准备好的PDO语句

5

我是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]:无效的参数数:绑定变量的数量与令牌数量不匹配
当我删除like子句和$searchterm参数时,它会正确返回结果。我认为“-- like'%?%'--”可能不是在双引号下创建此查询的合法方式,因此我尝试了转义',但没有成功。我寻找解决方案,并发现有人将'% 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]%"));
?> 

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


我有同样的问题,就像你一样,我认为“%”是查询结构的一部分,而不是值;) - Strae
3个回答

3
不要在绑定准备好的变量时添加引号,也不要绑定列名。
$query = sprintf( "select * from books where %s like ?", $searchtype );
...
$stmt->execute(array($searchtype, '%'.$searchterm.'%')); 

我尝试了这个,但它没有起作用。我认为问题就像bobince在另一个答案中所说的那样,它不会将%作为通配符,而是作为字面意义上的“%”,这并不是我的意图。 - Elle

2
$stmt->execute(array($searchtype, '\'%'.$searchterm.'%\'')); 

这不是参数化查询的工作方式。插入参数已经充当字面字符串,您不必在它们周围添加引号分隔符或转义它们(这就是整个重点),如果您尝试这样做,则会与字符串单引号搜索术语单引号进行比较。
因此,如果您(正如我所怀疑的那样)打算将特定列与字面字符串进行比较,则不需要对列名进行参数化。目前,您正在将一个字面字符串与另一个字面字符串进行比较,因此无论行中的数据如何,它都将始终为真或始终为假!
所以我认为您可能的意思是:
$query= "SELECT * FROM books WHERE $searchtype LIKE ?";
$like= "%$searchterm%";
$stmt->execute(array($like)); 

当涉及到SQL注入时,您需要非常小心确保$searchtype是已知可靠的。通常在使用它之前,您需要将其与可接受的列名列表进行比较。

(顺便说一下:有一种方法可以在模式名称中放置任意字符串,您可以用于列名,但这很麻烦,在不同的数据库中也有所变化,并且没有标准的转义函数。在MySQL中,您需要对反引号字符、引号和反斜杠进行反斜杠转义,并用反引号将名称括起来。在ANSI SQL中,您需要使用双引号和内部的双重双引号。在SQL Server中,您需要使用方括号。但实际上,您很少需要执行任何此操作,因为实际上您只需要允许一些预定义的列名。)

(另一个问题:如果您想允许$searchterm值包含文字百分号、下划线或反斜杠,使用户可以搜索“100%”而不匹配包含100的任何字符串,您需要使用显式转义字符,这有点繁琐:)

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

1
array('++', '+%', '+_') 不应该是 array('\+', '\%', '\\_') 吗?甚至可以只写成 array('\%', '\\_')(为什么要转义 + ?)。顺便问一下,为什么这里的 Markdown 将 \\_ 转换成了 _ ? - Halil Özgür
这个可以用!!! 但是如何去除空格(trim())呢? - user1775888

2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接