Вопрос

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

Возьмем этот пример:

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

Массив был бы, скажем,: $values = массив (1,2,3,4,5,6,7,8);

Безопасная для базы данных переменная будет иметь вид $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)

Конечно, $продукты был бы привязан к утверждению как :продукты.

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

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-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 как

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

и используйте этот массив $in в предложении IN

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

Вы можете сделать это очень легко.Если у вас есть массив значений для вашего оператора IN() НАПРИМЕР:

$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 REGEXP.

Например:

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

С уважением

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top