У меня проблема с 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)
Единственный способ, которым я могу это сделать, – поместить значения внутри самой строки, не связывая их, однако я точно знаю, что должен быть более простой способ сделать это.
Это то же самое, что было задано в этом вопросе: могу ли я привязать массив к условию 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, '\'', ''))
С уважением