74

我有一个关于 PDO 的问题,在被它困扰了很长一段时间后,我真的很想得到一个答案。

举个例子:

我将一组 ID 绑定到 PDO 语句以在 MySQL IN 语句中使用。

数组会说:$values = array(1,2,3,4,5,6,7,8);

数据库安全变量是$products = implode(',' $values);

因此,$products将是一个字符串,其值为:'1,2,3,4,5,6,7,8'

该声明将如下所示:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)

当然,$products将绑定到:products语句。

但是,当语句被编译并绑定值时,它实际上看起来像这样:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN ('1,2,3,4,5,6,7,8')

问题在于它将 IN 语句中的所有内容作为单个字符串执行,因为我已将其准备为逗号分隔值以绑定到语句。

我真正需要的是:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (1,2,3,4,5,6,7,8)

我实际上可以做到这一点的唯一方法是将值放在字符串本身中而不绑定它们,但是我肯定必须有一种更简单的方法来做到这一点。

4

8 回答 8

42

这与这个问题中提出的问题相同: 我可以将数组绑定到 IN() 条件吗?

答案是,对于in子句中的可变大小列表,您需要自己构建查询。

但是,您可以使用带引号的逗号分隔列表 using find_in_set,但对于大型数据集,这会产生相当大的性能影响,因为表中的每个值都必须转换为 char 类型。

例如:

select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)

或者,作为第三种选择,您可以创建一个用户定义的函数,为您拆分逗号分隔的列表(参见http://www.slickdev.com/2008/09/15/mysql-query-real-values- from-delimiter-separated-string-ids/)。这可能是三者中最好的选择,尤其是当您有很多依赖于in(...)子句的查询时。

于 2009-10-19T01:52:58.610 回答
27

处理这种情况的一个好方法是使用str_pad为 SQL 查询中的每个值放置一个?。然后,您可以将值数组(在您的情况下$values)作为要执行的参数传递:

$sql = '
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products.id IN ('.str_pad('',count($values)*2-1,'?,').')
';

$sth = $dbh->prepare($sql);
$sth->execute($values);
$user_ids = $sth->fetchAll();

这样,您可以从使用准备好的语句而不是直接将值插入 SQL 中获得更多好处。

PS - 如果具有给定 ID 的产品共享用户 ID,结果将返回重复的用户 ID。如果您只想要唯一的用户 ID,我建议将查询的第一行更改为SELECT DISTINCT users.id

于 2012-08-13T15:15:14.427 回答
9

您需要在IN中提供与$values数组中的值相同数量的?

这可以通过创建一个 ?s 数组来轻松完成

 $in = join(',', array_fill(0, count($values), '?'));

并在你的 IN 子句中使用这个 $in 数组

这将根据您不断变化的 $values 数组动态地为您提供量身定制的?s数组

于 2014-09-08T04:47:23.077 回答
8

在这种情况下,您可能会想到的最好的准备语句类似于以下内容:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)

然后,您将遍历您的值并将它们绑定到准备好的语句,确保问号的数量与您绑定的值相同。

于 2009-10-19T01:50:38.090 回答
2

你可以很容易地做到这一点。如果您的 IN() 语句 EG 有一组值:

$test = array(1,2,3);

你可以简单地做

$test = array(1,2,3);
$values = count($test);
$criteria = sprintf("?%s", str_repeat(",?", ($values ? $values-1 : 0)));
//Returns ?,?,?
$sql = sprintf("DELETE FROM table where column NOT IN(%s)", $criteria);
//Returns DELETE FROM table where column NOT IN(?,?,?)
$pdo->sth = prepare($sql);
$pdo->sth->execute($test);
于 2012-01-09T18:07:35.053 回答
1

如果表达式基于用户输入而不使用 bindValue() 绑定值,那么实验性 SQL 可能不是一个好的选择。但是,您可以通过使用 MySQL 的 REGEXP 检查输入的语法来使其安全。

例如:

SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
于 2012-10-22T21:29:57.620 回答
1

这是一个将未知数量的记录列绑定到插入值的示例。

public function insert($table, array $data)
{
    $sql = "INSERT INTO $table (" . join(',', array_keys($data)) . ') VALUES ('
        . str_repeat('?,', count($data) - 1). '?)';

    if($sth = $this->db->prepare($sql))
    {
        $sth->execute(array_values($data));
        return $this->db->lastInsertId();
    }
}

$id = $db->insert('user', array('name' => 'Bob', 'email' => 'foo@example.com'));
于 2012-11-24T20:09:27.397 回答
-4

请尝试这样:

WHERE products IN(REPLACE(:products, '\'', ''))

问候

于 2014-06-27T13:38:02.653 回答