MySQLIN 语句的 PDO 绑定值

我有一个与 PDO 的问题,我真的很想得到一个答案后,被它困扰了相当一段时间。

举个例子:

我将 ID 的数组绑定到一个 PDO 语句,以便在 MySQLIN 语句中使用。

数组应该是: $values = array (1,2,3,4,5,6,7,8) ;

数据库安全变量为 产品 = 内爆(’,’$值) ;

因此,$产品将是一个值为: ’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)

当然,$产品将作为 : 产品绑定到语句。

但是,当编译语句并绑定值时,它实际上是这样的:

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)

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

59292 次浏览

在这种情况下,你能想到的最好的陈述大概是这样的:

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

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

这和我们在这个问题中问到的是一样的: 我可以将数组绑定到 IN ()条件吗?

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

但是, 可以使用带引号、逗号分隔的列表使用 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(...)子句的查询。

你很容易就能做到。 如果 IN ()语句有一个值数组 例如:

$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);

处理这种情况的一个好方法是使用 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

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

例如:

SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'

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

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'));

请试试这样:

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

问候

您需要在 进去中提供与 $价值数组中的值数相同的 ? s数目

这可以很容易地通过创建一个? s as 数组来完成

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

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

这将根据变化中的 $value 数组动态地为您提供定制的 ? s数组