webentwicklung-frage-antwort-db.com.de

PDO-Bindungswerte für die MySQL-IN-Anweisung

Ich habe ein Problem mit PDO, für das ich wirklich gerne eine Antwort erhalten würde, nachdem ich einige Zeit von ihm geplagt wurde.

Nehmen Sie dieses Beispiel:

Ich binde ein Array von IDs an eine PDO-Anweisung zur Verwendung in einer MySQL-IN-Anweisung.

Das Array würde sagen: $ values ​​= array (1,2,3,4,5,6,7,8);

Die datenbanksichere Variable wäre $ products = implode (',' $ values);

Also wäre $ products dann ein STRING mit einem Wert von: '1,2,3,4,5,6,7,8'

Die Aussage würde folgendermaßen aussehen:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)

Natürlich wäre $ products an die Anweisung als: products gebunden.

Wenn die Anweisung jedoch kompiliert und an Werte gebunden ist, würde sie tatsächlich so aussehen:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN ('1,2,3,4,5,6,7,8')

Das Problem ist, dass es alles innerhalb der IN-Anweisung als einzelne Zeichenfolge ausführt, da ich es als durch Kommas getrennte Werte für die Bindung an die Anweisung vorbereitet habe.

Was ich eigentlich brauche ist:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (1,2,3,4,5,6,7,8)

Die einzige Möglichkeit, dies tatsächlich zu tun, besteht darin, die Werte innerhalb des Strings zu platzieren, ohne sie zu binden. Ich weiß jedoch, dass es einen einfacheren Weg geben muss.

69
hd82

Dies ist das gleiche wie in dieser Frage: Kann ich ein Array an eine IN () - Bedingung binden?

Die Antwort darauf war, dass Sie für eine Liste mit variabler Größe in der in-Klausel die Abfrage selbst erstellen müssen.

() Sie können jedoch die durch Kommas getrennte Liste mit find_in_set verwenden. Dies kann jedoch bei großen Datensätzen erhebliche Auswirkungen auf die Leistung haben, da jeder Wert in der Tabelle in a umgewandelt werden muss Char-Typ.

Zum Beispiel:

select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)

Oder als dritte Option Sie könnten eine benutzerdefinierte Funktion erstellen, die die durch Kommas getrennte Liste für Sie aufteilt (vgl. http://www.slickdev.com/2008/09/15/ mysql-query-real-values-from-delimiter-separierte-string-ids/ ). Dies ist wahrscheinlich die beste Option unter den dreien, insbesondere wenn Sie viele Abfragen haben, die auf in(...)-Klauseln beruhen.

37
James McNellis

Eine gute Möglichkeit, um mit dieser Situation umzugehen, ist die Verwendung von str_pad , um für jeden Wert in der SQL-Abfrage einen ? zu platzieren. Dann können Sie das Array von Werten (in Ihrem Fall $values) als auszuführendes Argument übergeben:

$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();

Auf diese Weise profitieren Sie mehr davon, vorbereitete Anweisungen zu verwenden, als die Werte direkt in die SQL einzufügen.

PS - Die Ergebnisse geben doppelte Benutzer-IDs zurück, wenn die Produkte mit den angegebenen IDs Benutzer-IDs verwenden. Wenn Sie nur eindeutige Benutzer-IDs wünschen, schlage ich vor, die erste Zeile der Abfrage in SELECT DISTINCT users.id zu ändern.

26
ghbarratt

Die am besten vorbereitete Aussage, die Sie in einer solchen Situation wahrscheinlich finden könnten, ähnelt der folgenden:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)

Sie würden dann Ihre Werte durchlaufen und sie an die vorbereitete Anweisung binden, um sicherzustellen, dass dieselbe Anzahl von Fragezeichen wie die Werte angegeben sind, die Sie binden.

7
Asaph

sie müssen dieselbe Anzahl von? s inINals Anzahl der Werte in Ihrem $ values ​​-Array angeben 

dies kann leicht durch Erstellen eines Arrays von? s als durchgeführt werden 

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

und verwenden Sie dieses $ in -Array in Ihrer IN-Klausel 

Dadurch erhalten Sie dynamisch ein maßgeschneidertes Array von? S gemäß Ihrem $ -Anzahlarray

5
ashsam786

Wenn der Ausdruck auf Benutzereingaben basiert, ohne die Werte mit bindValue () zu binden, ist experimentelles SQL möglicherweise keine gute Wahl. Aber , Sie können es sicher machen, indem Sie die Syntax der Eingabe mit MySQLs REGEXP überprüfen.

Zum Beispiel:

SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
1
Vunse

Sie können dies sehr leicht tun.

$test = array(1,2,3);

Sie können es einfach tun

$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);
1
fyrye

Hier ist ein Beispiel für das Binden einer unbekannten Anzahl von Datensatzspalten an Werte für ein Insert.

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]'));
0
Xeoncross