MySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Größe

Lesezeit: 7 Minuten

MySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Grose
smarthall

Wie würden Sie eine vorbereitete MySQL-Anweisung in PHP schreiben, die jedes Mal eine andere Anzahl von Argumenten akzeptiert? Ein Beispiel für eine solche Abfrage ist:

SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)

Die IN Klausel wird eine andere Anzahl von haben ids jedes Mal, wenn es ausgeführt wird.

Ich habe zwei mögliche Lösungen im Kopf, möchte aber sehen, ob es einen besseren Weg gibt.

Mögliche Lösung 1 Lassen Sie die Anweisung 100 Variablen akzeptieren und füllen Sie den Rest mit Dummy-Werten, die garantiert nicht in der Tabelle stehen; Führen Sie mehrere Aufrufe für mehr als 100 Werte durch.

Mögliche Lösung 2 Verwenden Sie keine vorbereitete Anweisung; Erstellen und führen Sie die Abfrage aus, die streng auf mögliche Einschleusungsangriffe prüft.

  • Ich vermute, ich würde beide von Ihnen vorgeschlagenen Lösungen vermeiden. Vorbereitete Anweisungen machen das Sichern Ihres Codes viel einfacher. Und Ihre erste Lösung scheint ziemlich ineffizient und verschwenderisch zu sein, wenn Sie eine kleine Liste haben.

    – Zoredache

    29. November 2008 um 9:40 Uhr

  • Ich stimme zu, dass ich dachte, dass diese Ideen den Verstand anderer anregen könnten. 🙂

    – smarthalle

    29. November 2008 um 12:17 Uhr

  • Informationen zu SQL Server finden Sie unter Parametrieren einer SQL IN-Klausel?

    – aussen

    4. April 2012 um 12:02 Uhr

MySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Grose
Zoredache

Mir fallen da ein paar Lösungen ein.

Eine Lösung könnte darin bestehen, eine temporäre Tabelle zu erstellen. Führen Sie für jeden Parameter, den Sie in der in-Klausel haben würden, eine Einfügung in die Tabelle durch. Führen Sie dann eine einfache Verknüpfung mit Ihrer temporären Tabelle durch.

Eine andere Methode könnte sein, so etwas zu tun.

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$parmcount=count($parms);   // = 4
$inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,?
$sql="SELECT age, name FROM people WHERE id IN (%s)";
$preparesql=sprintf($sql,$inclause);  // = example statement used in the question
$st=$dbh->prepare($preparesql);
$st->execute($parms);

Ich vermute, habe aber keinen Beweis dafür, dass die erste Lösung für größere Listen besser sein könnte und die spätere für kleinere Listen funktionieren würde.


Um @orrd glücklich zu machen, hier eine knappe Version.

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)',
                          implode(',',array_fill(0,count($parms),'?'))));
$st->execute($parms);

  • Dein zweiter Vorschlag gefällt mir. Tun Sie es und vergessen Sie es, bis die Leistung ein Problem darstellt. An diesem Punkt könnte es sich lohnen, die erste Option zu untersuchen.

    – benlumley

    29. November 2008 um 9:46 Uhr

  • Wenn ich nur daran gedacht hätte! Ihre erste Lösung klingt nach genau dem, wonach ich gesucht habe.

    – smarthalle

    29. November 2008 um 12:16 Uhr

  • Ich habe Muster Nr. 2 häufig verwendet. Die DBI von Perl verfügt über eine Prepare_cached()-Funktion. Wenn Sie also Abfragen mit einer ähnlichen Anzahl von Platzhaltern durchführen, werden Anweisungs-Handles wiederverwendet. Bei PHP bin ich mir aber nicht sicher..

    – Gary Richardson

    29. November 2008 um 23:33 Uhr

  • Das Erstellen einer temporären Tabelle wäre übertrieben, es sei denn, der Datensatz ist riesig. Der Beispielcode ist zu kompliziert (die Hälfte dieser Variablen kann eliminiert werden, und der Code kann erheblich vereinfacht werden, indem mehr Arbeit inline ausgeführt wird). Aber die Grundidee ist gut, indem Sie implode(‘,’,array_fill(0,count($params),’?’)) verwenden, um die “?”s zu generieren, und dann einfach die $params als zu bindende Daten übergeben .

    – bestell

    14. Januar 2014 um 23:49 Uhr

  • @orrd, meh … Als ich diese Antwort verfasste, versuchte ich, sie ausführlich zu machen, damit sie leicht verständlich ist. Ich stimme zu, dass es vereinfacht werden kann, aber ich glaube nicht, dass die von Ihnen vorgeschlagene Vereinfachung die Antwort leichter verständlich machen oder einen wesentlichen Einfluss auf die Leistung haben würde. In jedem Fall werde ich meine Antwort aktualisieren und eine kompakte Version für Sie hinzufügen.

    – Zoredache

    15. Januar 2014 um 8:12 Uhr

Es gibt auch die FIND_IN_SET Funktion dessen zweiter Parameter eine Zeichenfolge aus kommagetrennten Werten ist:

SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33')

  • Das Problem dabei ist: Es verwendet nicht den Primärindex und verwendet standardmäßig einen vollständigen Tabellenscan, während FIND_IN_SET für jede Zeile ausgeführt wird.

    – Hugo Maxwell

    26. Januar 2015 um 20:56 Uhr

Anständige SQL-Wrapper unterstützen die Bindung an Array-Werte. dh

$sql = "... WHERE id IN (?)";
$values = array(1, 2, 3, 4);
$result = $dbw -> prepare ($sql, $values) -> execute ();

  • Ich kenne eigentlich keine native PHP-Datenbankzugriffsbibliothek für MySQL (weder mysql, mysqli noch PDO), die das Binden von Parametern des Array-Typs ermöglicht.

    – Stefan Gehrig

    29. November 2008 um 10:09 Uhr

  • damals, als ich vor ein paar jahren in php entwickelt habe, hat adodb wirklich gute arbeit für mich geleistet. Ich denke, Sie sollten es überprüfen.

    – Eimantas

    29. November 2008 um 10:14 Uhr

  • Jedes Framework, das dies tut, erweitert die Liste und interpoliert sie in die SQL-Abfrage vor der Prepare()-Anweisung. Es ist nicht dasselbe wie gebundene Parameter.

    – Bill Karwin

    29. November 2008 um 17:53 Uhr

Bitte nehmen Sie Nr. 2 vom Tisch. Vorbereitete Anweisungen sind die einzige Möglichkeit, sich vor SQL-Injection zu schützen.

Sie können jedoch einen dynamischen Satz von Bindungsvariablen generieren. dh machen Sie nicht 100, wenn Sie 7 (oder 103) brauchen.

ich habe meine antwort bekommen von: http://bugs.php.net/bug.php?id=43568

das ist meine funktionierende Lösung für mein Problem. Jetzt kann ich dynamisch so viele Parameter verwenden, wie ich möchte. Sie werden die gleiche Nummer sein, die ich in einem Array habe, oder wie in diesem Fall übergebe ich die IDs von der letzten Abfrage (die alle IDs gefunden hat, bei denen email = “[email protected]”) an die dynamische Abfrage, um alle zu erhalten Die Informationen zu jeder dieser IDs, egal wie viele ich am Ende brauche.

<?php $NumofIds = 2; //this is the number of ids i got from the last query
    $parameters=implode(',',array_fill(0,$NumofIds,'?')); 
    // = ?,? the same number of ?'s as ids we are looking for<br />
    $paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/>
    //make the array to build the bind_param function<br/>
    $idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/>
    while($statement->fetch()){ //this is my last query i am getting the id out of<br/>
        $idAr[] = $id;  
    }

    //now this array looks like this array:<br/>
    //$idAr = array('ii', 128, 237);

    $query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)";
    $statement = $db->prepare($query);
    //build the bind_param function
    call_user_func_array (array($statement, "bind_param"), $idAr);
    //here is what we used to do before making it dynamic
    //statement->bind_param($paramtype,$v1,$v2);
    $statement->execute();
?>

1646645054 562 MySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Grose
Gemeinschaft

Wenn Sie nur ganzzahlige Werte in Ihrer verwenden IN -Klausel spricht nichts dagegen, Ihre Abfrage dynamisch ohne die Verwendung von SQL-Parametern zu erstellen.

function convertToInt(&$value, $key)
{
    $value = intval($value);
}

$ids = array('12', '45', '65', '33');
array_walk($ids, 'convertToInt');
$sql="SELECT age, name FROM people WHERE id IN (" . implode(', ', $ids) . ')';
// $sql will contain  SELECT age, name FROM people WHERE id IN (12, 45, 65, 33)

Aber ohne Zweifel ist die Lösung hier die allgemeinere Herangehensweise an dieses Problem.

1646645054 562 MySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Grose
Gemeinschaft

Ich hatte heute ein ähnliches Problem und bin auf dieses Thema gestoßen. Als ich mir die Antworten ansah und in Google herumsuchte, fand ich eine hübsche Lösung.

Allerdings ist mein Problem etwas komplizierter. Denn ich habe feste Bindungswerte und auch Dynamik.

Das ist die Lösung.

$params = array()
$all_ids = $this->get_all_ids();

for($i = 0; $i <= sizeof($all_ids) - 1; $i++){
    array_push($params, $all_ids[$i]['id']);
}

$clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ?
$total_i = implode('', array_fill(0, count($params), 'i')); // output iiii

$types = "ss" . $total_i; // will reproduce : ssiiii ..etc

// %% it's necessary because of sprintf function
$query = $db->prepare(sprintf("SELECT * 
                                FROM clients    
                                WHERE name LIKE CONCAT('%%', ?, '%%') 
                                AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%')
                                AND id IN (%s)", $clause));

$thearray = array($name, $description);
$merge    = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4

// We need  to pass variables instead of values by reference
// So we need a function to that
call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge))); 

Und das Funktion makeValuesreferenced:

public function makeValuesReferenced($arr){
    $refs = array();
    foreach($arr as $key => $value)
        $refs[$key] = &$arr[$key];
    return $refs;
}

Links, um dieses „Know-how“ zu erhalten: https://bugs.php.net/bug.php?id=49946PHP hängt ein Array an ein anderes an (nicht array_push oder +), [PHP]: Error -> Zu wenige Argumente in sprintf();, http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171Pass-by-Reference-Problem mit PHP 5.3.1

964770cookie-checkMySQL Vorbereitete Anweisungen mit einer Variablenliste variabler Größe

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy