Значения привязки 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 66

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'));

пожалуйста, попробуйте такой:

WHERE products IN(REPLACE(:products, '\'', ''))

в отношении