Использование WHERE IN (…) с PDO не работает, когда строка привязана

У меня есть запрос, который выглядит так:

UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (:idString) 

Где idString – это строка идентификаторов, разделенных запятыми, и передается в execute() в массиве. К моему удивлению, когда этот запрос выполняется, обновляется только строка с первым id в idString .

Несколько секунд стукнув головой о стену, я, наконец, решил попробовать:

  UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (' . $idString . ') 

Второй запрос работает так, как ожидалось.

Почему запрос не будет работать, когда я свяжу строку идентификаторов с помощью PDO?

Solutions Collecting From Web of "Использование WHERE IN (…) с PDO не работает, когда строка привязана"

В SQL строка

 '1,2,3,5,12' 

Это единственное значение и литье его в числовом контексте, оно просто будет иметь значение ведущих цифр, поэтому просто значение 1 .

Это сильно отличается от множества нескольких значений:

 '1', '2', '3', '5', '12' 

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

Если вы хотите передать набор нескольких значений параметрам в вашем SQL-запросе, у вас должно быть несколько заполнитель параметров:

 UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (:id1, :id2, :id3, :id4, :id5) 

Затем взорвите строку значений и передайте их как массив:

 $idlist = array('id1' => 1, 'id2' => 2, 'id3' => 3, 'id4' => 5, 'id5' => 12); $pdoStmt->execute($idlist); 

В таких случаях я бы рекомендовал использовать позиционные параметры вместо названных параметров, потому что вы можете передать простой массив вместо ассоциативного массива:

 $pdoStmt = $pdo->prepare("UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (?, ?, ?, ?, ?)"); $idlist = explode(",", "1,2,3,5,12"); $pdoStmt->execute($idlist); 

@mario добавляет комментарий, который вы можете использовать FIND_IN_SET () . Этот запрос будет выглядеть так, чтобы вы могли передать одну строку, отформатированную как строка значений, разделенных запятыми:

 $pdoStmt = $pdo->prepare("UPDATE table SET column = UNIX_TIMESTAMP() WHERE FIND_IN_SET(id, :idString)"); $pdoStmt->execute(["idString" => "1,2,3,5,12"]); 

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

Ваше рабочее решение не так хорошо, поскольку оно подвержено SQL INJECTION .
Причина, по которой он не работает, заключается в том, что вы выделяете массив вместо обычных значений, разделенных запятыми.

Вы должны использовать implode для разделения значений массива, а затем присваивать значения, разделенные запятыми, переменной, которую можно использовать pdo .

В противном случае вы можете использовать вместо : $idString ? in the select statement, and executing the prepared statement from and array which holds the $idString.

  $ query = $ db-> prepare ("Выберите a Из таблицы, где b =? order by 1;");
  $ Query-> выполнить (массив ($ idString)); 

Вы пытаетесь передать строку в виде набора в подготовленный оператор. MySQL пытается выполнить запрос

 -- assuming idString is "1,2,3,4,5" UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN ("1,2,3,4,5"); 

вместо

 UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (1,2,3,4,5); 

вам придется либо использовать инструкцию

 UPDATE table SET column = UNIX_TIMESTAMP() WHERE id == ? 

и выполнить его для любого количества идентификаторов, которые у вас есть, или подготовить инструкцию, введя строку id в запрос

При связывании PDO ожидает одно значение. Что-то вроде этого будет работать, учитывая ваш $ idString выше (хотя, если у вас есть исходный массив, еще лучше!):

 $ids = explode(',', $idString); $placeholders = implode(', id', array_keys($ids)); if($placeholders) { $placeholders = 'id' . $placeholders; $sql = "UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN ({$placeholders})"; // prepare your statement, yielding "$st", a \PDOStatement $st = $pdo->prepare($sql); // bind every placeholder foreach($ids as $key => $id) { $st->bindValue("id{$key}", $id); } // execute $st->execute(); }