Значения привязки PDO для MySQL в инструкции
у меня есть проблема с PDO, что я действительно хотел бы получить ответ на после того, как его мучают в течение довольно долгого времени.
рассмотрим пример:
я привязываю массив идентификаторов к оператору PDO для использования в операторе MySQL IN.
массив будет сказать: $ values = array (1,2,3,4,5,6,7,8);
безопасная для базы данных переменная будет $products = implode ( ' , ' $values);
Так, $ products было бы тогда строка стоимостью: '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)
единственный способ, которым я могу это сделать, - это поместить значения в саму строку, не связывая их, однако я точно знаю, что должен быть более простой способ сделать это.
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-ids/)это, вероятно, лучший вариант из трех, особенно если у вас есть много запросов, которые полагаются на
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 на на как количество значений $ values массив
Это можно сделать легко, создав массив ?s как
$in = join(',', array_fill(0, count($values), '?'));
и используйте этот массив $in в вашем предложении IN
это динамически предоставит вам индивидуальный массив ?s согласно вашему массиву changiing $values
Вы можете сделать это очень легко. Если у вас есть массив значений в() заявление Например:
$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 может быть не лучшим выбором. но, вы можете сделать его безопасным, проверив синтаксис ввода с регулярным выражением 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'));