MySQL IN语句的PDO绑定值

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

以这个例子:

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

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

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

那么, $产品将会是一个值为“1,2,3,4,5,6,7,8”STRING

声明如下所示:

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语句内的所有内容作为一个单一的string,因为我已经准备好它作为逗号分隔值绑定到语句。

我真正需要的是:

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

我唯一能做到这一点的方法是将这些值放在string本身中,而不必绑定它们,但是我知道肯定有一个更简单的方法来做到这一点。

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

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

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

例如:

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

或者,作为第三个选项,您可以创build一个用户定义的函数,为您分割逗号分隔列表(参见http://www.slickdev.com/2008/09/15/mysql-query-real-values- from-delimiter-separated-string-ids / )。 这可能是三者中最好的select,特别是如果你有很多依赖in(...)子句的查询。

处理这种情况的一个好方法是使用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,我build议将查询的第一行改为SELECT DISTINCT users.id

在这样的情况下,你可能想到的最好的准备就是类似于以下内容:

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

然后,您将循环访问您的值,并将其绑定到准备好的语句,确保问号的数量与绑定的值相同。

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

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

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

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

这将根据您的changiing $ values数组dynamic地为您提供量身定制的数组

你可以很容易地做到这一点。 如果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); 

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

例如:

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

下面是将未知数量的logging列绑定到插入值的示例。

 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, '\'', '')) 

问候