PDO-bindingswaarden voor MySQL IN-instructie

Ik heb een probleem met PDO waar ik heel graag een antwoord op zou willen krijgen nadat ik er geruime tijd mee geplaagd werd.

Neem dit voorbeeld:

Ik bind een array van ID’s aan een PDO-statement voor gebruik in een MySQL IN-statement.

De array zou zijn: $values ​​= array(1,2,3,4,5,6,7,8);

De databaseveilige variabele is $products = implode(‘,’ $values);

Dus, $productszou dan een STRINGzijn met een waarde van: ‘1,2,3,4,5,6,7,8 ‘

De verklaring zou er als volgt uitzien:

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

Natuurlijk zou $productsgebonden zijn aan de verklaring als :products.

Als het statement echter is gecompileerd en de waarden zijn gebonden, ziet het er in werkelijkheid zo uit:

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

Het probleem is dat alles binnen de IN-instructie als een enkele string wordt uitgevoerd, aangezien ik het heb voorbereid als door komma’s gescheiden waarden om aan de instructie te binden.

Wat ik eigenlijk nodig heb is:

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

De enige manier waarop ik dit kan doen, is door de waarden in de tekenreeks zelf te plaatsen zonder ze te binden, maar ik weet zeker dat er een eenvoudigere manier moet zijn om dit te doen.


Antwoord 1, autoriteit 100%

Dit is hetzelfde als in deze vraag werd gesteld: Kan ik een array binden aan een IN()-voorwaarde?

Het antwoord was dat je voor een lijst met variabele afmetingen in de in-component de query zelf moet maken.

U kunt echterde geciteerde, door komma’s gescheiden lijst gebruiken met find_in_set, hoewel dit voor grote datasets een aanzienlijke prestatie-impact zou hebben, aangezien elke waarde in de tabel moet worden gecast naar een tekentype.

Bijvoorbeeld:

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

Of, als derde optie,kunt u een door de gebruiker gedefinieerde functie maken die de door komma’s gescheiden lijst voor u splitst (zie http://www.slickdev.com/2008/09/15/mysql- query-real-values-from-delimiter-separated-string-ids/). Dit is waarschijnlijk de beste optie van de drie, vooral als je veel zoekopdrachten hebt die afhankelijk zijn van in(...)-clausules.


Antwoord 2, autoriteit 64%

Een goede manier om met deze situatie om te gaan is om str_padom een ​​?te plaatsen voor elke waarde in de SQL-query. Vervolgens kunt u de reeks waarden (in uw geval $values) doorgeven als het uit te voeren argument:

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

Op deze manier haalt u meer voordeel uit het gebruik van voorbereide instructies in plaats van de waarden rechtstreeks in de SQL in te voegen.

PS – De resultaten zullen dubbele gebruikers-ID’s opleveren als de producten met de gegeven ID’s gebruikers-ID’s delen. Als u alleen unieke gebruikers-ID’s wilt, raad ik u aan de eerste regel van de zoekopdracht te wijzigen in SELECT DISTINCT users.id


Antwoord 3, autoriteit 21%

u moet hetzelfde aantal ?sin INopgeven als het aantal waarden in uw $valuesarray

dit kan eenvoudig worden gedaan door een array van ?s te maken als

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

en gebruik deze $in array in je IN-clausule

Dit zal u dynamisch voorzien van een op maat gemaakte array van ?svolgens uw veranderende $values ​​array


Antwoord 4, autoriteit 19%

De best voorbereide verklaring die u waarschijnlijk kunt bedenken in een situatie als deze, is iets dat lijkt op het volgende:

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

U loopt dan door uw waarden en bindt ze aan de voorbereide verklaring, waarbij u ervoor zorgt dat er hetzelfde aantal vraagtekens is als de waarden die u bindt.


Antwoord 5, autoriteit 5%

Je kunt dit heel gemakkelijk doen.
Als je een reeks waarden hebt voor je IN()-instructie
Bv:

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

Je kunt het gewoon doen

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

Antwoord 6, autoriteit 2%

Als de expressie is gebaseerd op gebruikersinvoer zonder de waarden te binden met bindValue(), is experimentele SQL mogelijk geen goede keuze. Maar, je kunt het veilig maken door de syntaxis van de invoer te controleren met REGEXP van MySQL.

Bijvoorbeeld:

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

Antwoord 7, autoriteit 2%

Hier is een voorbeeld van het binden van een onbekend aantal recordkolommen aan waarden voor een 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]'));

Antwoord 8

Probeer het als volgt:

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

Met vriendelijke groeten

Other episodes