PHP – использование PDO с массивом предложений IN

Я использую PDO для выполнения оператора с предложением IN , которое использует массив для его значений:

 $in_array = array(1, 2, 3); $in_values = implode(',', $in_array); $my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (".$in_values.")"); $my_result->execute(); $my_results = $my_result->fetchAll(); 

Вышеприведенный код работает отлично, но мой вопрос в том, почему это не так:

  $in_array = array(1, 2, 3); $in_values = implode(',', $in_array); $my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (:in_values)"); $my_result->execute(array(':in_values' => $in_values)); $my_results = $my_result->fetchAll(); 

Этот код вернет элемент who's my_value равный первому элементу в $in_array (1), но не остальные элементы в массиве (2 и 3).

PDO не очень хорош в таких вещах. Вам нужно создать строку с вопросительными знаками динамически и вставить в запрос.

 $in = str_repeat('?,', count($in_array) - 1) . '?'; $sql = "SELECT * FROM my_table WHERE my_value IN ($in)"; $stm = $db->prepare($sql); $stm->execute($in_array); $data = $stm->fetchAll(); - $in = str_repeat('?,', count($in_array) - 1) . '?'; $sql = "SELECT * FROM my_table WHERE my_value IN ($in)"; $stm = $db->prepare($sql); $stm->execute($in_array); $data = $stm->fetchAll(); 

Если в запросе есть другие заполнители, вы можете использовать следующий подход (код берется из моего учебника PDO ):

Вы можете использовать array_merge() чтобы объединить все переменные в один массив, добавив другие переменные в виде массивов в том порядке, в котором они появляются в вашем запросе:

 $arr = [1,2,3]; $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?"; $stm = $db->prepare($sql); $params = array_merge([$foo], $arr, [$bar, $baz]); $stm->execute($params); $data = $stm->fetchAll(); - $arr = [1,2,3]; $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?"; $stm = $db->prepare($sql); $params = array_merge([$foo], $arr, [$bar, $baz]); $stm->execute($params); $data = $stm->fetchAll(); 

Если вы используете именованные заполнители, код будет немного сложнее, так как вам нужно создать последовательность :id0,:id1,:id2 заполнителей, например :id0,:id1,:id2 . Таким образом, код будет:

 // other parameters that are going into query $params = ["foo" => "foo", "bar" => "bar"]; $ids = [1,2,3]; $in = ""; foreach ($ids as $i => $item) { $key = ":id".$i; $in .= "$key,"; $in_params[$key] = $item; // collecting values into key-value array } $in = rtrim($in,","); // :id0,:id1,:id2 $sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar"; $stm = $db->prepare($sql); $stm->execute(array_merge($params,$in_params)); // just merge two arrays $data = $stm->fetchAll(); - // other parameters that are going into query $params = ["foo" => "foo", "bar" => "bar"]; $ids = [1,2,3]; $in = ""; foreach ($ids as $i => $item) { $key = ":id".$i; $in .= "$key,"; $in_params[$key] = $item; // collecting values into key-value array } $in = rtrim($in,","); // :id0,:id1,:id2 $sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar"; $stm = $db->prepare($sql); $stm->execute(array_merge($params,$in_params)); // just merge two arrays $data = $stm->fetchAll(); 

К счастью, для названных заполнителей нам не нужно следовать строгому порядку, поэтому мы можем объединить наши массивы в любом порядке.

Подстановка переменных в подготовленных инструкциях PDO не поддерживает массивы. Это один на один.

Вы можете обойти эту проблему, создав необходимое количество заполнителей на основе длины массива.

 $variables = array ('1', '2', '3'); $placeholders = str_repeat ('?, ', count ($variables) - 1) . '?'; $query = $pdo -> prepare ("SELECT * FROM table WHERE column IN($placeholders)"); if ($query -> execute ($variables)) { // ... } 

Насколько я понимаю, это связано с тем, что PDO будет обрабатывать содержимое $ in_values ​​как отдельный элемент и будет вполне соответствующим образом. PDO увидит 1,2,3 как одну строку, поэтому запрос будет выглядеть примерно так:

SELECT * FROM table WHERE my_value IN ("1,2,3")

Вы можете подумать, что изменение impode на наличие цитат и запятых исправит его, но это не так. PDO увидит цитаты и изменит, как она цитирует строку.

Что касается того, почему ваш запрос соответствует первому значению, у меня нет объяснений.

Я только что столкнулся с этой проблемой и закодировал небольшую обертку. Это не самый красивый или лучший код, я уверен, но он может помочь кому-то, вот он вот так:

 function runQuery(PDO $PDO, string $sql, array $params = []) { if (!count($params)) { return $PDO->query($sql); } foreach ($params as $key => $values) { if (is_array($values)) { // get placeholder from array, eg ids => [7,12,3] would be ':ids' $oldPlaceholder = ':'.$key; $newPlaceholders = ''; $newParams = []; // loop through array to create new placeholders & new named parameters for($i = 1; $i <= count($values); $i++) { // this gives us :ids1, :ids2, :ids3 etc $newKey = $oldPlaceholder.$i; $newPlaceholders .= $newKey.', '; // this builds an associative array of the new named parameters $newParams[$newKey] = $values[$i - 1]; } //trim off the trailing comma and space $newPlaceholders = rtrim($newPlaceholders, ', '); // remove the old parameter unset($params[$key]); // and replace with the new ones $params = array_merge($params, $newParams); // amend the query $sql = str_replace($oldPlaceholder, $newPlaceholders, $sql); } } $statement = $PDO->prepare($sql); $statement->execute($params); return $statement; } не function runQuery(PDO $PDO, string $sql, array $params = []) { if (!count($params)) { return $PDO->query($sql); } foreach ($params as $key => $values) { if (is_array($values)) { // get placeholder from array, eg ids => [7,12,3] would be ':ids' $oldPlaceholder = ':'.$key; $newPlaceholders = ''; $newParams = []; // loop through array to create new placeholders & new named parameters for($i = 1; $i <= count($values); $i++) { // this gives us :ids1, :ids2, :ids3 etc $newKey = $oldPlaceholder.$i; $newPlaceholders .= $newKey.', '; // this builds an associative array of the new named parameters $newParams[$newKey] = $values[$i - 1]; } //trim off the trailing comma and space $newPlaceholders = rtrim($newPlaceholders, ', '); // remove the old parameter unset($params[$key]); // and replace with the new ones $params = array_merge($params, $newParams); // amend the query $sql = str_replace($oldPlaceholder, $newPlaceholders, $sql); } } $statement = $PDO->prepare($sql); $statement->execute($params); return $statement; } 

Например, передавая их в:

 SELECT * FROM users WHERE userId IN (:ids) array(1) { ["ids"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } } 

становится:

 SELECT * FROM users WHERE userId IN (:ids1, :ids2, :ids3) array(3) { [":ids1"]=> int(1) [":ids2"]=> int(2) [":ids3"]=> int(3) } 

Это не пуленепробиваемый, но в качестве единственного разработчика для моих потребностей он отлично работает, пока все равно.