Wie kann ich viele Zeilen in eine MySQL-Tabelle einfügen und die neuen IDs zurückgeben?

75

Normalerweise kann ich eine Zeile in eine MySQL-Tabelle einfügen und die last_insert_idRückseite erhalten. Jetzt möchte ich jedoch viele Zeilen in die Tabelle einfügen und ein Array von IDs zurückerhalten. Weiß jemand, wie ich das machen kann?

Es gibt einige ähnliche Fragen, aber sie sind nicht genau gleich. Ich möchte die neue ID nicht in eine temporäre Tabelle einfügen. Ich möchte nur das Array von IDs zurückbekommen.

Kann ich die lastInsertId aus einer Masseneinfügung abrufen?

MySQL-Anweisung zum Einfügen und Auswählen mehrerer Zeilen mit last_insert_id ()

Friedensstiftung
quelle
Warum können Sie sie nicht einzeln einfügen?
Sanmai
Sie müssen die OUTPUTKlausel simulieren . Ich glaube, Sie können dies mit einem Trigger in MySQL tun
Martin Smith
Sie können keine Masseneinfügung verwenden und dann eine Funktion lime last_insert_id (), es funktioniert nicht.
NB
Mögliches Duplikat der OUTPUT-Klausel in MySQL
Martin Smith
4
Nein - diese Methode ist auch fehlerhaft - es sei denn, Sie verpacken die Einfügung in eine LOCK TABLES ... WRITE, und Sie sollten auch auto_increment_increment
symcbean

Antworten:

76

Alter Thread, der sich aber nur damit befasst hat. Wenn Sie InnoDB in einer neueren Version von MySQL verwenden, können Sie die Liste der IDs mit LAST_INSERT_ID()und abrufen ROW_COUNT().

InnoDB garantiert fortlaufende Nummern für AUTO INCREMENT bei Masseneinfügungen, sofern diese innodb_autoinc_lock_modeauf 0 (traditionell) oder 1 (aufeinanderfolgend) gesetzt sind. Folglich können Sie die erste ID von LAST_INSERT_ID()und die letzte durch Hinzufügen erhalten ROW_COUNT()-1.

Dag Sondre Hansen
quelle
6
Benötigen Sie eine Transaktion? Was wäre, wenn andere Beilagen in die Mitte kämen?
3
Solange Sie die InnoDB-Engine verwenden und sicherstellen, dass die automatische Sperrung aktiviert ist (was zu einer langsameren Leistung führen kann), ist keine explizite Transaktionsdefinition erforderlich. Da InnoDB während des Einfügens eine Sperre durchführt, müssen andere Einfügungen warten, bis die Einfügung abgeschlossen ist. Werfen
Dag Sondre Hansen
1
MyISAM ist keine Transaktions-DB-Engine, und daher können andere Einfügungen auftreten, während die Masseneinfügung ausgeführt wird, wodurch die Sequenz durcheinander gebracht wird. Das Sperren kann dies verhindern (obwohl nicht sicher), aber die einzige Lösung, die ich gefunden habe und die von MySQL dokumentiert wird, ist die oben beschriebene.
Dag Sondre Hansen
1
@ Wulf Ich nehme an (unterstrichen angenommen), es bezieht sich auf EINFÜGEN, LADEN VON DATEN, ERSETZEN IN usw.
Dag Sondre Hansen
1
Ich denke, es ist auch wichtig zu beachten, dass Sie, wenn Sie sich auf eine Methode wie diese verlassen, um mehrere Insert-IDs zu erhalten, keine Multi-Master-Cluster wie Galera Cluster verwenden können, falls Sie jemals so etwas benötigen.
JohnK
17

Ich kann mir nur vorstellen, dass dies möglich ist, wenn Sie für jeden Satz eingefügter Zeilen (guid) eine eindeutige Kennung speichern und dann die Zeilen-IDs auswählen. z.B:

INSERT INTO t1
(SELECT col1,col2,col3,'3aee88e2-a981-1027-a396-84f02afe7c70' FROM a_very_large_table);
COMMIT;

SELECT id FROM t1 
WHERE guid='3aee88e2-a981-1027-a396-84f02afe7c70';

Sie können die Guid auch in der Datenbank mithilfe von generieren uuid()

Kevin Burton
quelle
4

Nehmen wir an, wir haben eine Tabelle namens Versuchbar mit zwei Spalten uid, col1, wobei uid ein automatisches Inkrementfeld ist. Wenn Sie wie folgt vorgehen, werden alle in die Ergebnismenge eingefügten IDs zurückgegeben. Sie können die Ergebnismenge durchlaufen und Ihre IDs abrufen. Mir ist klar, dass dies ein alter Beitrag ist und diese Lösung möglicherweise nicht in jedem Fall funktioniert. Aber für andere könnte es sein und deshalb antworte ich darauf.

# lock the table
lock tables temptable write;

#bulk insert the rows;
insert into temptable(col1) values(1),(2),(3),(4);

#get the value of first inserted row. when bulk inserting last_insert_id() #should give the value of first inserted row from bulk op.
set @first_id = last_insert_id();

#now select the auto increment field whose value is greater than equal to #the first row. Remember since you have write lock on that table other #sessions can't write to it. This resultset should have all the inserted #id's
select uid from temptable where uid >=@first_id;

#now that you are done don't forget to unlock the table.
unlock tables;
Sonnenuntergang
quelle
0

Ich denke, Sie müssen entweder die Transaktions-ID in Ihrer Anwendung oder die Artikel-ID in Ihrer Anwendung verarbeiten, um dies fehlerfrei zu tun.

Ein Weg, dies zu tun, der funktionieren könnte, vorausgesetzt, alle Ihre Einfügungen sind erfolgreich (!), Ist der folgende:

Sie können dann die eingefügten IDs mit einer Schleife für die Anzahl der betroffenen Zeilen abrufen, beginnend mit lastid (dies ist die erste eingefügte ID der Masseneinfügung). Und so habe ich überprüft, ob es perfekt funktioniert. Sei nur vorsichtig, dass HeidiSQL zum Beispiel nicht den richtigen Wert für ROW_COUNT () zurückgibt, wahrscheinlich weil es eine beschissene GUI ist, die zufällige Scheiße macht Kommandozeile oder PHP mysqli -

START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');
SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;
COMMIT;

In PHP sieht es so aus (local_sqle ist ein direkter Aufruf von mysqli_query, local_sqlec ist ein Aufruf von mysqli_query + Konvertieren der Ergebnismenge in ein PHP-Array):

local_sqle("START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');");
$r=local_sqlec("SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;");
local_sqle("
COMMIT;");
$i=0;
echo "last id =".($r[0]['lastid'])."<br>";
echo "Row count =".($r[0]['rowcount'])."<br>";

while($i<$r[0]['rowcount']){
    echo "inserted id =".($r[0]['lastid']+$i)."<br>";
    $i++;
}

Der Grund, warum die Abfragen getrennt sind, liegt darin, dass ich mein Ergebnis sonst nicht mit meinen eigenen Funktionen erhalten würde. Wenn Sie dies mit Standardfunktionen tun, können Sie es wieder in eine Anweisung setzen und dann das gewünschte Ergebnis abrufen (es sollte die Ergebnisnummer sein 2 - vorausgesetzt, Sie verwenden eine Erweiterung, die mehr als eine Ergebnismenge / Abfrage verarbeitet).

Morg.
quelle
0

Ich wäre mir nicht sicher, ob der Wert für das automatische Inkrementieren das Element um 1 erhöht. Es wird große Probleme geben, wenn Ihre Datenbank über eine Master // Master-Replikation verfügt und der doppelte Ausschluss von auto_increment behoben wird. KI wird +2 statt +1 sein, auch wenn es noch einen Meister gibt, wird es zu +3 kommen. Wenn Sie also etwas weitergeben, wie AUTO_INCREMENT für 1 steigt, wird Ihr Projekt beendet.

Ich sehe nur einige gute Möglichkeiten, dies zu tun.

Dieses SQL-Snippet hat keine Probleme mit mehreren Mastern und liefert gute Ergebnisse, bis Sie nur noch eingefügte Datensätze benötigen. Bei mehreren Anforderungen ohne Transaktionen können andere Einfügungsdatensätze abgefangen werden.

START TRANSACTION;
SELECT max(id) into @maxLastId FROM `main_table`;
INSERT INTO `main_table` (`value`) VALUES ('first'), ('second') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);
SELECT `id` FROM `main_table` WHERE id > @maxLastId OR @maxLastId IS NULL;
COMMIT;

(Wenn Sie auch aktualisierte Datensätze von DUPLICATE KEY UPDATE benötigen), müssen Sie die Datenbank ein wenig umgestalten, und SQL sieht wie folgt aus (sicher für Transaktionen und keine Transaktionen innerhalb einer Verbindung).

#START TRANSACTION    
INSERT INTO bulk_inserts VALUES (null);
SET @blukTransactionId = LAST_INSERT_ID();
SELECT  @blukTransactionId, LAST_INSERT_ID();
INSERT INTO `main_table` (`value`, `transaction_id`) VALUES ('first', @blukTransactionId), ('second', @blukTransactionId) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `transaction_id` = VALUES(`transaction_id`);
SELECT  @blukTransactionId, LAST_INSERT_ID();
SELECT id FROM `main_table` WHERE `transaction_id` = @blukTransactionId;
#COMMIT

Beide Fälle sind transnational sicher. Erstens werden nur eingefügte Datensätze angezeigt und zweitens werden alle Datensätze sogar aktualisiert.

Auch diese Optionen funktionieren auch mit INSERT IGNORE ...

Neznajka
quelle
0

Dieser Thread ist alt, aber all diese Lösungen haben mir nicht geholfen, also habe ich mir meine eigenen ausgedacht.

Zählen Sie zunächst, wie viele Zeilen Sie einfügen möchten

Nehmen wir an, wir müssen 5 Zeilen hinzufügen:

LOCK TABLE tbl WRITE;

SELECT `AUTO_INCREMENT` FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'my_db' AND TABLE_NAME   = 'tbl'

Verwenden Sie dann das soeben ausgewählte auto_increment, um die nächste Abfrage durchzuführen:

ALTER TABLE tbl AUTO_INCREMENT = {AUTO_INCREMENT}+5;
UNLOCK TABLES;

Zum Schluss machen Sie Ihre Einsätze

Verwenden Sie den reservierten Autoincrement-Bereich, um eine ID einzufügen.

Warnung: Diese Lösung erfordert eine erhöhte Zugriffsebene auf die Tabellen. Aber normalerweise werden Bulk-Einfügungen von Crones und Importer-Skripten ausgeführt, und was nicht, kann ohnehin einen speziellen Zugriff erfordern. Sie würden dies nicht nur für ein paar Beilagen verwenden.

Dies kann dazu führen, dass nicht verwendete IDs nicht verwendet werden, wenn Sie ON DUPLICATE KEY UPDATE verwenden.

Paul G Mihai
quelle
-11
$query = "INSERT INTO TABLE (ID,NAME,EMAIL) VALUES (NULL,VALUE1, VALUE2)";
$idArray = array();
foreach($array as $key) {
 mysql_query($query);
 array_push($idArray, mysql_insert_id());
}
print_r($idArray);
Ajayendra
quelle
3
Es ist keine Massenabfrage.
Peacemoon