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

时间:2010-10-08 17:59:35

标签: php sql pdo prepared-statement

我是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参数时,它会正确返回结果。我想 - 比如'%?%' - 可能不是用双引号创建这个查询的合法方式,所以我尝试转义',这不起作用。我四处寻找解决方案,发现有人将'%和%'移到了$ 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]%"));
?> 

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

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

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

这不是参数化查询的工作方式。插入的参数已经作为文字字符串,你不必在它们周围添加引号分隔符或者转义它们(这就是整点),如果你尝试,你实际上是在比较字符串单引号-searchterm-single-报价。

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

所以我认为你的意思是:

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

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

(旁白:一种将任意字符串放在可用于列的模式名称中的方法,但它很烦人,因数据库而异,并且没有标准的转义函数在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%")); 

答案 2 :(得分:2)

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