Bei doppelter Schlüsselaktualisierung wie beim Einfügen

158

Ich habe mich umgesehen, aber nicht gefunden, ob es möglich ist.

Ich habe diese MySQL-Abfrage:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

Die Feld-ID hat einen "eindeutigen Index", daher können nicht zwei davon vorhanden sein. Wenn dieselbe ID bereits in der Datenbank vorhanden ist, möchte ich sie aktualisieren. Aber muss ich wirklich alle diese Felder noch einmal angeben, wie:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

Oder:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Ich habe alles schon in der Beilage angegeben ...

Ein zusätzlicher Hinweis, ich möchte die Arbeit nutzen, um die ID zu bekommen!

id=LAST_INSERT_ID(id)

Ich hoffe, jemand kann mir sagen, was der effizienteste Weg ist.

Roy
quelle
9
AFAIK, die Methode zum Anpassen jeder Spalte, a=VALUES(a), b=VALUES(b), ...ist der Weg, den Sie gehen müssen. So mache ich das für alle meine INSERT ON DUPLICATE KEY UPDATEAussagen.
Valdogg21
4
Zur Verdeutlichung ist alles, was hier angegeben wird, korrekt, solange Sie verstehen, dass die zu beantwortende Frage lautet: Müssen Sie jede Spalte, die Sie aktualisieren möchten, neu formulieren? Ja, wenn Sie 8 Spalten in eine Tabelle mit 25 Spalten einfügen oder aktualisieren möchten, müssen Sie die 8 Spalten zweimal angeben - einmal im Einfügeteil und einmal im Aktualisierungsteil. (Sie können den Primärschlüssel im Aktualisierungsteil überspringen, aber es ist am einfachsten, eine Zeichenfolge "a = 1, b = 2, c = 3" vorzuformatieren und zweimal einzubetten.) Die verbleibenden 17 Spalten müssen jedoch NICHT erneut angepasst werden du willst dich nicht ändern Wenn es sich um ein UPDATE handelt, behalten sie ihre vorhandenen Werte bei.
Timberline
3
Ich habe diesen Kommentar hinzugefügt, weil die Fragen und Antworten mich über nicht erwähnte Spalten mit dieser Art von INSERT unsicher machten, die ich dann getestet habe, nachdem ich genau gelernt hatte, was zu testen ist. INSERT ON DUPLICATE KEY UPDATE lässt nicht erwähnte Spalten bei UPDATE unverändert, gibt ihnen jedoch Standardwerte bei INSERT. Mit REPLACE INTO erhalten nicht erwähnte Spalten immer Standardwerte, niemals vorhandene Werte.
Timberline
1
Überprüfen Sie stackoverflow.com/questions/2472229/… , ich denke, es gibt, was Sie suchen
Chococroc

Antworten:

82

Die UPDATEAnweisung wird angegeben, damit ältere Felder auf einen neuen Wert aktualisiert werden können. Wenn Ihre älteren Werte mit Ihren neuen übereinstimmen, warum sollten Sie sie dann auf jeden Fall aktualisieren?

Zum Beispiel. Wenn Ihre Spalten azu gbereits eingestellt als 2auf 8; Es wäre nicht erforderlich, es erneut zu aktualisieren.

Alternativ können Sie verwenden:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Um das idvon zu bekommen LAST_INSERT_ID; Sie müssen die Backend-App angeben, die Sie für dieselbe verwenden.

Für LuaSQL ruft a conn:getlastautoid()den Wert ab.

hjpotter92
quelle
6
Es ist nur für das Beispiel. In einer realen Abfrage gibt es Unterschiede. Meine Frage ist ganz einfach: Muss ich wirklich alle Felder (und Werte in meinem ersten Beispiel) angeben, wenn sie mit denen in der Einfügung übereinstimmen? Ich möchte nur alle einfügen oder wenn es eine eindeutige Wertübereinstimmung gibt: Alle aktualisieren.
Roy
1
"Ein zusätzlicher Hinweis, ich würde gerne die Arbeit nutzen, um auch den Ausweis zu bekommen!"
Agamemnus
1
@Tresdin, soweit ich das beurteilen kann, handelt es sich nur um syntaktischen Zucker. Persönlich habe ich noch nie jemanden gesehen, der a = VALUES (a), b = VALUES (b) usw. verwendet. Vielleicht können Sie der Spalte mehrere Werte zuweisen? Ich bin mir nicht sicher, warum Sie die Daten nicht vorher verketten würden.
DuckPuncher
54
Wenn die Tabelle tblbereits die Zeile (1,10) enthält, setzt: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)a = 2 . Während INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=awird ein = 10 setzen
Bùi Việt Thành
2
Warum all die positiven Stimmen? Das funktioniert nicht. Wie @ Bùi Việt Thành betonte, verwendet mit a=aUpdates nichts. (Die Abfragen in der ursprünglichen Frage aktualisieren auch nichts, aber ein Update scheint das zu sein, was das OP beabsichtigt hat.)
Roger Dueck
41

Es gibt eine MySQL-spezifische Erweiterung für SQL, die genau das ist, was Sie wollen - REPLACE INTO

Es funktioniert jedoch nicht ganz so wie 'ON DUPLICATE UPDATE'

  1. Es löscht die alte Zeile, die mit der neuen Zeile kollidiert, und fügt dann die neue Zeile ein. Solange Sie keinen Primärschlüssel in der Tabelle haben, wäre das in Ordnung. Wenn Sie dies jedoch tun, verweist eine andere Tabelle auf diesen Primärschlüssel

  2. Sie können nicht auf die Werte in den alten Zeilen verweisen, daher können Sie kein Äquivalent dazu erstellen

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Ich würde gerne die Arbeit nutzen, um die ID zu bekommen!

Das sollte funktionieren - last_insert_id () sollte den richtigen Wert haben, solange Ihr Primärschlüssel automatisch inkrementiert wird.

Wie gesagt, wenn Sie diesen Primärschlüssel tatsächlich in anderen Tabellen verwenden, REPLACE INTOist dies für Sie wahrscheinlich nicht akzeptabel, da dadurch die alte Zeile gelöscht wird, die über den eindeutigen Schlüssel in Konflikt geraten ist.

Jemand anderes hat vorgeschlagen, bevor Sie die Eingabe reduzieren können, indem Sie Folgendes tun:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
quelle
Ich kann mich also vor einer replace intoAbfrage nicht an den Primärschlüssel halten ?
Roy
Nein - diese Zeile wird gelöscht und eine neue Zeile erstellt, die einen neuen Primärschlüssel hat.
Danack
Würde dies nicht den Prozess bei großen Datenmengen verlangsamen und Sie möchten CSVs mit 100K oder mehr Zeilen importieren?
Muhammad Omer Aslam
24

Es gibt keinen anderen Weg, ich muss alles zweimal angeben. Erstens für die Einfügung, zweitens für den Update-Fall.

Roy
quelle
9
Dies ist falsch und sollte nicht die akzeptierte Antwort sein. Die Antwort von @Danack ist die richtige Antwort.
Wayne Workman
2
Sie sollten die Frage @WayneWorkman erneut lesen, dies ist die richtige Antwort.
Roy
24

Hier ist eine Lösung für Ihr Problem:

Ich habe versucht, ein Problem wie das Ihre zu lösen, und ich möchte vorschlagen, es unter einfachen Gesichtspunkten zu testen.

Befolgen Sie diese Schritte: Lernen Sie aus einer einfachen Lösung.

Schritt 1: Erstellen Sie mit dieser SQL-Abfrage ein Tabellenschema :

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Eine Tabelle <code> user </ code> mit Daten

Schritt 2: Erstellen Sie einen Index aus zwei Spalten , um doppelte Daten mithilfe der folgenden SQL-Abfrage zu vermeiden:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

oder Erstellen Sie einen Index mit zwei Spalten über die GUI wie folgt: Erstellen Sie einen Index über die GUI Wählen Sie Spalten aus, um einen Index zu erstellen Indizes der Tabelle <code> user </ code>

Schritt 3: Aktualisieren, falls vorhanden, einfügen, wenn keine der folgenden Abfragen verwendet werden:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Tabelle <code> Benutzer </ code> nach dem Ausführen der obigen Abfrage


quelle
1
Vielen Dank für diese exemplarische Vorgehensweise - hat mich verständlich gemacht, sehr geschätzt, danke!
Michael Nilsson
Zu Ihrer Information: Das Speichern von Passwörtern im Klartext ist eine schreckliche Idee, ebenso wie der Versuch, doppelte Passwörter auf Datenbankebene zu verhindern.
OrangeDog
10

Nur für den Fall, dass Sie eine Skriptsprache zum Vorbereiten Ihrer SQL-Abfragen verwenden können, können Sie Feld-Wert-Paare SETanstelle von verwenden (a,b,c) VALUES(a,b,c).

Ein Beispiel mit PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Beispieltabelle:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Chris
quelle
4
Sie sollten Ihren Werten entkommen oder eine vorbereitete Anweisung mit Ihren Werten ausführen.
Chinoto Vokro
1
@ChinotoVokro - ich stimme zu. Dies ist nur ein "Wie Sie könnten" und verwendet im Beispiel nicht einmal Abfragefunktionen.
Chris
1
Dies ist eine gute Alternative zum Angeben eines Arrays von Schlüsseln und eines Arrays von Werten. Vielen Dank.
Mark Carpenter Jr
5

Möglicherweise möchten Sie die Verwendung der REPLACE INTOSyntax in Betracht ziehen , aber seien Sie gewarnt, wenn Sie den PRIMARY / UNIQUE-Schlüssel duplizieren, wird die Zeile gelöscht und eine neue eingefügt .

Sie müssen nicht alle Felder neu angeben. Sie sollten jedoch die mögliche Leistungsreduzierung in Betracht ziehen (abhängig von Ihrem Tischdesign).

Vorsichtsmaßnahmen:

  • Wenn Sie den Primärschlüssel AUTO_INCREMENT haben, erhält er einen neuen
  • Indizes müssen wahrscheinlich aktualisiert werden
Matej
quelle
Es liegt eine Einschränkungsverletzung vor, wenn der Index auch ein Fremdschlüssel für eine andere Tabelle ist und durch einen Löschteil ausgelöst wird.
user3290180
4

Ich weiß, dass es spät ist, aber ich hoffe, dass jemandem bei dieser Antwort geholfen wird

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Sie können das Tutorial hier lesen:

https://mariadb.com/kb/de/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/

Galang Piliang
quelle
Gute Antwort. Die Verwendung von VALUES () zum Verweisen auf die neuen Zeilen ist jedoch ab MySQL 8.0.20 veraltet . Der neue Ansatz, der unter dem Link beschrieben wird, besteht darin, einen Alias ​​für die Zeile zu verwenden. In diesem Beispiel lautet der Alias new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473
@ user697473 Ich erhalte die Fehlermeldung: "Sie haben einen Fehler in Ihrer SQL-Syntax. Überprüfen Sie das Handbuch, das Ihrer MariaDB-Serverversion entspricht, auf die richtige Syntax für 'AS new ON DUPLICATE KEY UPDATE a = new.a'." und meine Abfrage funktionierte (außer wenn offensichtlich doppelte Schlüssel auftraten), bevor ich diese Klausel implementierte. Irgendwelche Ideen?
Aaron
@ Aaron - Entschuldigung, keine großartigen Ideen. MySQL 8.0.20 wurde erst Ende April veröffentlicht. Vielleicht hat MariaDB noch nicht aufgeholt. Das könnte erklären, warum es Ihnen einen Fehler gibt.
user697473
@ user697473 Ja, ich habe gerade die Methode a = VALUES (a) in der ursprünglichen Antwort ausprobiert und es hat funktioniert, danke trotzdem.
Aaron
-7

Sie können Insert Ignorieren für einen solchen Fall verwenden. Es wird ignoriert, wenn doppelte Datensätze abgerufen werden. INSERT IGNORE ...; - ohne ON DUPLICATE KEY

pitu
quelle
Ich wollte es einfügen, wenn es nicht vorhanden ist, sonst aktualisieren. Nicht ignorieren.
Roy
warum Sie es aktualisieren möchten, wenn Sie mit dem vorherigen / gleichen Wert aktualisieren, wenn es ein neuer Wert ist, dann ist es
pitu
2
Höchstwahrscheinlich, weil sich die Werte ändern können und er einen Datensatz erstellen möchte, wenn einer nicht vorhanden ist, aber einen vorhandenen Datensatz aktualisieren möchte, wenn dies (mit Änderungen) der Fall ist, ohne dass ein Roundtrip zurück zur Anwendung und dann zurück zur Datenbank erforderlich ist nochmal.
Mopsyd