Значения привязки PDO для оператора MySQL IN
У меня проблема с PDO, что я действительно хотел бы получить ответ за то, что вас так долго мучает.
Возьмем этот пример:
Я связываю массив ID с инструкцией PDO для использования в инструкции MySQL IN.
Массив будет говорить: $values = array (1,2,3,4,5,6,7,8);
Переменная, безопасная для базы данных, будет $products = 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)
Единственный способ, которым я действительно могу это сделать, - поместить значения внутри самой строки, не связывая их, однако я знаю, что для этого должен быть более простой способ сделать это.
Ответы
Ответ 1
Это то же самое, что было задано в этом вопросе: Могу ли я привязать массив к условию 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(...)
.
Ответ 2
Хорошим способом справиться с этой ситуацией является использование 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
Ответ 3
Лучшее подготовленное утверждение, которое вы, вероятно, могли бы найти в такой ситуации, похоже на следующее:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)
Затем вы пропустите свои значения и привяжите их к подготовленному оператору, убедившись, что существует такое же количество вопросительных знаков, что и значения, которые вы связываете.
Ответ 4
вам нужно предоставить такое же количество ? s в IN как количество значений в массиве $
это можно сделать легко, создав массив? s как
$in = join(',', array_fill(0, count($values), '?'));
и используйте этот массив $в вашем разделе IN
Это будет динамически предоставлять вам индивидуальный массив ? s в соответствии с вашим изменяющимся массивом $values
Ответ 5
Вы можете сделать это очень легко.
Если у вас есть массив значений для вашего оператора 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);
Ответ 6
Если выражение основано на пользовательском вводе без привязки значений с bindValue(), экспериментальный SQL не может быть отличным выбором. Но вы можете сделать это безопасным, проверив синтаксис ввода с помощью MySQL REGEXP.
Например:
SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
Ответ 7
Вот пример привязки неизвестного количества столбцов записи к значениям для вставки.
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' => '[email protected]'));
Ответ 8
Пожалуйста, попробуйте вот так:
WHERE products IN(REPLACE(:products, '\'', ''))
Привет