Ich verwende derzeit diese Art von SQL unter MySQL, um mehrere Wertezeilen in eine einzige Abfrage einzufügen:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Bei den Lesungen zu PDO sollten die vorbereiteten Anweisungen zur Verwendung eine bessere Sicherheit bieten als statische Abfragen.
Ich würde daher gerne wissen, ob es möglich ist, mit vorbereiteten Anweisungen "Einfügen mehrerer Wertzeilen mithilfe einer Abfrage" zu generieren.
Wenn ja, kann ich wissen, wie ich es implementieren kann?
php
pdo
insert
prepared-statement
hoball
quelle
quelle
$stmt->execute($data);
php.net/manual/en/… Grundsätzlich werden alle Parameter als Strings validiert übergeben. Durchlaufen Sie einfach die Daten, nachdem Sie die Abfrage erstellt haben, und übergeben Sie den Typ manuellbindValue
oderbindParam
übergeben Sie ihn als drittes Argument.Antworten:
Mehrere Werte mit PDO-vorbereiteten Anweisungen einfügen
Einfügen mehrerer Werte in eine Ausführungsanweisung. Warum, weil es laut dieser Seite schneller ist als normale Einfügungen.
mehr Datenwerte oder Sie haben wahrscheinlich eine Schleife, die Daten auffüllt.
Bei vorbereiteten Einfügungen müssen Sie die Felder kennen, in die Sie einfügen, und die Anzahl der Felder, um die? Platzhalter zum Binden Ihrer Parameter.
So soll die insert-Anweisung im Grunde aussehen.
Nun der Code:
Obwohl es in meinem Test nur einen Unterschied von 1 Sekunde gab, wenn mehrere Einsätze und regelmäßig vorbereitete Einsätze mit einem einzigen Wert verwendet wurden.
quelle
placeholders()
immer wieder anzurufen. Rufen Sie es einmal vor der Schleife mit aufsizeof($datafields)
und hängen Sie die Ergebniszeichenfolge an$question_marks[]
die Schleife an.Gleiche Antwort wie Herr Balagtas, etwas klarer ...
Neuere Versionen von MySQL und PHP PDO tun Unterstützung mehrreihigen
INSERT
Aussagen.SQL-Übersicht
Das SQL sieht ungefähr so aus, vorausgesetzt, Sie möchten eine 3-Spalten-Tabelle erstellen
INSERT
.ON DUPLICATE KEY UPDATE
funktioniert wie erwartet auch mit einem mehrzeiligen INSERT; füge dies hinzu:PHP Übersicht
Ihr PHP-Code folgt dem üblichen
$pdo->prepare($qry)
und$stmt->execute($params)
PDO-Aufrufen.$params
wird ein eindimensionales Array aller Werte sein, die an das übergeben werden sollenINSERT
.Im obigen Beispiel sollte es 9 Elemente enthalten. PDO verwendet jeden 3er-Satz als einzelne Wertereihe. (Einfügen von 3 Zeilen mit jeweils 3 Spalten = 9 Elementarray.)
Implementierung
Der folgende Code wurde aus Gründen der Klarheit und nicht der Effizienz geschrieben. Arbeite mit dem PHP
array_*()
Funktionen, um Ihre Daten besser abzubilden oder zu durchsuchen, wenn Sie möchten. Ob Sie Transaktionen verwenden können, hängt natürlich von Ihrem MySQL-Tabellentyp ab.Angenommen:
$tblName
- Der Zeichenfolgenname der Tabelle, in die INSERT eingefügt werden soll$colNames
- 1-dimensionales Array der Spaltennamen der Tabelle Diese Spaltennamen müssen gültige MySQL-Spaltenbezeichner sein. Entkomme ihnen mit Backticks (``), wenn sie es nicht sind$dataVals
- mehrdimensionales Array, wobei jedes Element ein 1-d-Array einer Wertezeile für INSERT istBeispielcode
quelle
$rowPlaces
nicht mehr benötigt werden:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
ADD UNIQUEunique_index
(user
,email
,address
);array_push($dataToInsert, ...array_values($dataVals));
dann viel schneller seinforeach ($dataVals as $row => $data) {}
Für das, was es wert ist, haben viele Benutzer empfohlen, INSERT-Anweisungen zu durchlaufen, anstatt wie die ausgewählte Antwort eine einzelne Zeichenfolgenabfrage zu erstellen. Ich habe beschlossen, einen einfachen Test mit nur zwei Feldern und einer sehr einfachen Einfügeanweisung durchzuführen:
Während die Gesamtabfrage selbst Millisekunden oder weniger dauerte, war die letztere Abfrage (einzelne Zeichenfolge) durchweg achtmal schneller oder länger. Wenn dies so aufgebaut wäre, dass es einen Import von Tausenden von Zeilen in viel mehr Spalten widerspiegelt, könnte der Unterschied enorm sein.
quelle
bind_param
in der zweiten Importroutine keine Anweisung"?(?,?)
, oder?Die akzeptierte Antwort von Herbert Balagtas funktioniert gut, wenn das $ data-Array klein ist. Bei größeren $ data-Arrays wird die Funktion array_merge unerschwinglich langsam. Meine Testdatei zum Erstellen des $ data-Arrays enthält 28 Spalten und ungefähr 80.000 Zeilen. Das endgültige Skript dauerte 41 Sekunden .
Die Verwendung von array_push () zum Erstellen von $ insert_values anstelle von array_merge () führte zu einer 100-fachen Beschleunigung mit einer Ausführungszeit von 0,41 s .
Das problematische array_merge ():
Um die Notwendigkeit von array_merge () zu beseitigen, können Sie stattdessen die folgenden zwei Arrays erstellen:
Diese Arrays können dann wie folgt verwendet werden:
quelle
array_push($data, ...array_values($row))
stattdessen tun$data = array_merge($data, array_values($row));
. Viel schneller.array_push()
ist sogar in PHP 4 verfügbar.array_push()
, sondern weil @Mark das Entpacken von Argumenten verwendet. Beachten Sie den...array_values()
Anruf dort?array_values()
ist auch in PHP 4 verfügbar. Ich bin mir nicht sicher, ob du das meinstargument unpacking
.Zwei mögliche Ansätze:
Oder:
Wenn sich die Daten für alle Zeilen in einem einzigen Array befinden, würde ich die zweite Lösung verwenden.
quelle
$stmt->execute();
vor, außerhalb der foreach-Schleife zu sein?So verwenden Sie einfach keine vorbereiteten Anweisungen.
Es ist vollkommen in Ordnung, eine Zeile pro Abfrage einzufügen, da Sie eine vorbereitete Anweisung mehrmals mit unterschiedlichen Parametern ausführen können. Dies ist in der Tat einer der größten Vorteile, da Sie auf effiziente, sichere und komfortable Weise eine große Anzahl von Zeilen einfügen können.
Es ist also möglicherweise möglich, das von Ihnen vorgeschlagene Schema zumindest für eine feste Anzahl von Zeilen zu implementieren, aber es ist fast garantiert, dass dies nicht wirklich das ist, was Sie wollen.
quelle
Eine kürzere Antwort: Reduzieren Sie dann das nach Spalten geordnete Datenarray
Wenn Sie etwa 1.000 Datensätze einfügen, müssen Sie nicht jeden Datensatz durchlaufen, um sie einzufügen, wenn Sie nur die Anzahl der Werte benötigen.
quelle
Hier ist mein einfacher Ansatz.
quelle
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
, die$value
s mit ziemlich unerwarteten Daten haben können. Sie können nicht garantieren, dass dies möglicherweise nicht jetzt, aber in Zukunft ein anderer Entwickler diese Daten unsicher macht. Daher halte ich die von PDO vorbereitete Abfrage für viel richtiger.Hier ist eine Klasse, die ich geschrieben habe, um mehrere Einfügungen mit Löschoption durchzuführen:
quelle
So habe ich es gemacht:
Definieren Sie zuerst die Spaltennamen, die Sie verwenden möchten, oder lassen Sie sie leer, und pdo geht davon aus, dass Sie alle Spalten in der Tabelle verwenden möchten. In diesem Fall müssen Sie die Zeilenwerte in der genauen Reihenfolge angeben, in der sie in der Tabelle angezeigt werden .
Angenommen, Sie haben bereits ein zweidimensionales Array vorbereitet. Iterieren Sie es und erstellen Sie eine Zeichenfolge mit Ihren Zeilenwerten als solche:
Jetzt haben Sie nur überprüft, ob $ rows bereits definiert wurde. Wenn nicht, erstellen Sie es und speichern Sie die Zeilenwerte und die erforderliche SQL-Syntax, damit es sich um eine gültige Anweisung handelt. Beachten Sie, dass Zeichenfolgen in doppelte und einfache Anführungszeichen gesetzt werden sollten, damit sie sofort als solche erkannt werden.
Sie müssen lediglich die Anweisung vorbereiten und als solche ausführen:
Bisher mit bis zu 2000 Zeilen getestet, und die Ausführungszeit ist schlecht. Ich werde noch einige Tests durchführen und hierher zurückkehren, falls ich noch etwas beitragen kann.
Grüße.
quelle
Da dies noch nicht vorgeschlagen wurde, bin ich mir ziemlich sicher, dass LOAD DATA INFILE immer noch der schnellste Weg ist, Daten zu laden, da es die Indizierung deaktiviert, alle Daten einfügt und dann die Indizes wieder aktiviert - alles in einer einzigen Anforderung.
Das Speichern der Daten als CSV sollte unter Berücksichtigung von fputcsv ziemlich trivial sein. MyISAM ist am schnellsten, aber in InnoDB erhalten Sie immer noch eine große Leistung. Es gibt jedoch noch andere Nachteile, daher würde ich diesen Weg gehen, wenn Sie viele Daten einfügen und sich nicht mit weniger als 100 Zeilen beschäftigen.
quelle
Obwohl mir eine alte Frage alle Beiträge sehr geholfen haben, ist hier meine Lösung, die in meiner eigenen
DbContext
Klasse funktioniert . Der$rows
Parameter ist einfach ein Array von assoziativen Arrays, die Zeilen oder Modelle darstellen :field name => insert value
.Wenn Sie ein Muster verwenden, das Modelle verwendet, passt dies gut, wenn Modelldaten als Array übergeben werden, z. B. von einer
ToRowArray
Methode innerhalb der Modellklasse.quelle
$tableName
es dem Benutzer ausgesetzt ist, was es nicht ist, es ist in der DAL. Können Sie Ihre Ansprüche erweitern? Es ist nicht hilfreich, nur Dinge zu sagen.$tableName
?Hier ist eine andere (schlanke) Lösung für dieses Problem:
Zuerst müssen Sie die Daten des Quellarrays (hier: $ aData) mit count () zählen. Dann verwenden Sie array_fill () und generieren ein neues Array mit so vielen Einträgen wie das Quellarray mit dem Wert "(?,?)" (Die Anzahl der Platzhalter hängt von den verwendeten Feldern ab; hier: 2). Dann muss das erzeugte Array implodiert werden und als Klebstoff wird ein Komma verwendet. Innerhalb der foreach-Schleife müssen Sie einen weiteren Index für die Anzahl der verwendeten Platzhalter generieren (Anzahl der Platzhalter * aktueller Array-Index + 1). Sie müssen dem generierten Index nach jedem gebundenen Wert 1 hinzufügen.
quelle
Mit dieser Funktion können Sie mehrere Zeilen in eine einzelne Abfrage einfügen:
$ row ist ein Array von Arrays von Werten. In Ihrem Fall würden Sie die Funktion mit aufrufen
Dies hat den Vorteil, dass Sie vorbereitete Anweisungen verwenden , während Sie mehrere Zeilen mit einer einzigen Abfrage einfügen. Sicherheit!
quelle
Hier ist meine Lösung: https://github.com/sasha-ch/Aura.Sql basierend auf der Bibliothek auraphp / Aura.Sql.
Anwendungsbeispiel:
Bugreports sind willkommen.
quelle
Mein Beispiel aus der Praxis, um alle deutschen Postleitzahlen in eine leere Tabelle einzufügen (um später Städtenamen hinzuzufügen):
Wie Sie sehen können, ist es völlig flexibel. Sie müssen weder die Anzahl der Spalten noch die Position Ihrer Spalte überprüfen. Sie müssen nur die Einfügedaten einstellen:
Ich bin stolz auf einige der Abfragezeichenfolgenkonstruktoren, da sie ohne umfangreiche Array-Funktionen wie array_merge funktionieren. Besonders vsprintf () war ein guter Fund.
Schließlich musste ich 2x while () hinzufügen, um das Speicherlimit nicht zu überschreiten. Dies hängt von Ihrem Speicherlimit ab, ist aber eine gute allgemeine Lösung, um Probleme zu vermeiden (und 10 Abfragen sind immer noch viel besser als 10.000).
quelle
test.php
Database.php
quelle
Ich hatte das gleiche Problem und so erreiche ich es für mich selbst, und ich habe eine Funktion für mich selbst dafür gemacht (und Sie können es verwenden, wenn das Ihnen hilft).
Beispiel:
INSERT IN LÄNDER (Land, Stadt) WERTE (Deutschland, Berlin), (Frankreich, Paris);
Wenn insertMultipleData ($ table, $ multi_params) TRUE zurückgibt , wurden Ihre Daten in Ihre Datenbank eingefügt.
quelle
Basierend auf meinen Experimenten fand ich heraus, dass die MySQL-Insert-Anweisung mit mehreren Wertzeilen in einer einzelnen Transaktion die schnellste ist.
Wenn die Daten jedoch zu groß sind, kann die
max_allowed_packet
Einstellung von mysql die Einfügung einer einzelnen Transaktion mit mehreren Wertzeilen einschränken. Daher schlagen die folgenden Funktionen fehl, wenn Daten vorhanden sind, die größer als MySQLmax_allowed_packet
sind:singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
Das erfolgreichste Szenario zum Einfügen großer Datenmengen ist die
transactionSpeed
Methode, die jedoch mehr Zeit in Anspruch nimmt als die oben genannten Methoden. Um dieses Problem zu lösen, können Sie Ihre Daten entweder in kleinere Blöcke aufteilen und die Einfügung einer einzelnen Transaktion mehrmals aufrufen oder die Ausführungsgeschwindigkeit mithilfe dertransactionSpeed
Methode aufgeben .Hier ist meine Forschung
Die Ergebnisse für 100.000 Einträge für eine Tabelle mit nur zwei Spalten sind wie folgt
quelle
Das hat bei mir funktioniert
quelle
was ist mit so etwas:
Die Idee dahinter ist, durch Ihre Array-Werte zu blättern und jeder Schleife "ID-Nummern" für Ihre vorbereiteten Anweisungsplatzhalter hinzuzufügen, während Sie gleichzeitig die Werte für die Bindungsparameter zu Ihrem Array hinzufügen. Wenn Sie den "Schlüssel" -Index aus dem Array nicht verwenden möchten, können Sie $ i = 0 und $ i ++ in die Schleife einfügen. Beides funktioniert in diesem Beispiel, selbst wenn Sie assoziative Arrays mit benannten Schlüsseln haben, würde es trotzdem funktionieren, vorausgesetzt, die Schlüssel sind eindeutig. Mit ein wenig Arbeit wäre es auch für verschachtelte Arrays in Ordnung.
** Beachten Sie, dass substr das letzte Leerzeichen und Komma der $ sql-Variablen entfernt. Wenn Sie kein Leerzeichen haben, müssen Sie dies in -1 anstatt in -2 ändern.
quelle
Die meisten der hier angegebenen Lösungen zum Erstellen der vorbereiteten Abfrage sind komplexer als erforderlich. Mit den in PHP integrierten Funktionen können Sie die SQL-Anweisung problemlos und ohne erheblichen Aufwand erstellen.
Bei
$records
einem Array von Datensätzen, bei denen jeder Datensatz selbst ein indiziertes Array (in Form vonfield => value
) ist, fügt die folgende Funktion die Datensätze in der angegebenen Tabelle$table
über eine PDO-Verbindung$connection
mit nur einer einzigen vorbereiteten Anweisung ein. Beachten Sie, dass dies eine PHP 5.6+ -Lösung ist, da beim Aufruf vonarray_push
:quelle