Значения привязки PDO для оператора MySQL IN

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

Возьмем следующий пример:

Я связываю массив идентификаторов с инструкцией PDO для использования в инструкции MySQL IN.

Массив будет говорить: $ values ​​= array (1,2,3,4,5,6,7,8);

Безопасной для базы данных будет $ product = implode (',' $ values);

Таким образом, $ products тогда будет STRING со значением: '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 будет привязано к заявлению как : продукты .

Однако, когда оператор компилируется и привязаны значения, он будет выглядеть следующим образом:

 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) 

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

Solutions Collecting From Web of "Значения привязки PDO для оператора MySQL 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-id / ). Вероятно, это лучший вариант для трех, особенно если у вас много запросов, на которые опираются 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. Результаты возвращают повторяющиеся идентификаторы пользователей, если продукты с указанными идентификаторами разделяют идентификаторы пользователей. Если вам нужны только уникальные идентификаторы пользователей, я предлагаю изменить первую строку запроса на SELECT DISTINCT users.id

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

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

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

вам нужно предоставить такое же количество ? s в IN, сколько количество значений в вашем массиве $ values

это можно сделать легко, создав массив? s как

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

и использовать этот массив $ в вашем разделе IN

Это будет динамически предоставлять вам индивидуальный массив в соответствии с вашим изменяющимся массивом $ values

Вы можете сделать это очень легко. Если у вас есть массив значений для вашего оператора 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); 

Если выражение основано на пользовательском вводе без привязки значений с помощью bindValue (), экспериментальный SQL не может быть отличным выбором. Но вы можете сделать это безопасным, проверив синтаксис ввода с REGEXP MySQL.

Например:

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

С уважением