MySQL Conditional Insert

98

Es fällt mir schwer, ein bedingtes INSERT zu bilden

Ich habe x_table mit Spalten (Instanz, Benutzer, Element), in denen die Instanz-ID eindeutig ist. Ich möchte nur dann eine neue Zeile einfügen, wenn der Benutzer bereits kein bestimmtes Element hat.

Zum Beispiel versuchen, Instanz = 919191 Benutzer = 123 Element = 456 einzufügen

Insert into x_table (instance, user, item) values (919191, 123, 456) 
    ONLY IF there are no rows where user=123 and item=456 

Jede Hilfe oder Anleitung in die richtige Richtung wäre sehr dankbar.

Das Unbekannte
quelle

Antworten:

115

Wenn Ihr DBMS keine Einschränkungen für die Tabelle auferlegt, aus der Sie beim Ausführen einer Einfügung auswählen, versuchen Sie Folgendes:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE NOT EXISTS (SELECT * FROM x_table
                             WHERE user = 123 
                               AND item = 456)

Darin dualbefindet sich eine Tabelle mit nur einer Zeile (ursprünglich in Oracle, jetzt auch in MySQL). Die Logik besteht darin, dass die SELECT-Anweisung eine einzelne Datenzeile mit den erforderlichen Werten generiert, jedoch nur, wenn die Werte noch nicht gefunden wurden.

Alternativ können Sie sich die MERGE-Anweisung ansehen.

Jonathan Leffler
quelle
1
Muss dualin diesem Fall vorab erstellt werden oder handelt es sich um eine spezielle "temporäre" Tabelle, die von der Abfrage nur für die Verwendung der Abfrage erstellt wurde?
itsmequinn
8
Wenn Sie es noch nicht getan haben dual, müssen Sie es erstellen. CREATE TABLE dual ( dummy CHAR(1) DEFAULT 'x' NOT NULL CHECK (dummy = 'x') PRIMARY KEY ); INSERT INTO dual VALUES('x'); REVOKE ALL ON dual FROM PUBLIC; GRANT SELECT ON dual TO PUBLIC;
Jonathan Leffler
Vielen Dank, das ist großartig zu wissen
itsmequinn
11
Beachten Sie, dass Sie in MySQL nicht unbedingt eine Tabelle mit dem Namen 'dual' benötigen, um zu existieren: Es handelt sich um einen speziellen Tabellennamen, mit dem Sie etwas daraus auswählen können.
Christopher Schultz
3
MySQL "respektiert" das Konzept von "dual", aber es existiert nicht wirklich. Zum Beispiel, wenn Sie SELECT COUNT(*) FROM dualimmer bekommen 1, und wenn SELECT 'foo' FROM dualSie immer bekommen foo. Aber du kannst nicht SELECT * FROM dualund du kannst nicht DESCRIBE dualoder so etwas. Ich habe es nicht überprüft, aber ich glaube auch nicht, dass Sie Berechtigungen für widerrufen können dual. Es ist also erwähnenswert, dass es so funktioniert, wie Sie es erwarten ... außer wenn es nicht funktioniert;)
Christopher Schultz
52

Sie können auch verwenden, bei INSERT IGNOREdem die Einfügung stillschweigend ignoriert wird, anstatt eine Zeile zu aktualisieren oder einzufügen, wenn Sie einen eindeutigen Index für (Benutzer, Element) haben.

Die Abfrage sieht folgendermaßen aus:

INSERT IGNORE INTO x_table(instance, user, item) VALUES (919191, 123, 456)

Sie können den eindeutigen Index mit hinzufügen CREATE UNIQUE INDEX user_item ON x_table (user, item).

MrD
quelle
2
Gemäß MySQL-Dokumentation ist INSERT IGNORE das Gegenstück zu REPLACE ... INSERT IGNORE: Behalten Sie alte Zeilen bei. ERSETZEN: Neue Zeilen behalten. Beide arbeiten mit eindeutigen Schlüsseln.
Paul Kenjora
Die beste Antwort aller Zeiten
jmojico
30

Haben Sie jemals so etwas versucht?

INSERT INTO x_table
SELECT 919191 as instance, 123 as user, 456 as item
FROM x_table
WHERE (user=123 and item=456)
HAVING COUNT(*) = 0;
Tempel
quelle
Oh ja ! intelligente und nette Lösung mit nur einer SELECT-Anweisung
Java Acm
Nett. Dies funktioniert für postgresql beim ersten Versuch, bei dem die akzeptierte Antwort für mich nicht zu sein schien.
mächtiger
10

Mit a UNIQUE(user, item), mach:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=123

Das user=123Bit ist ein "No-Op", das der Syntax der ON DUPLICATEKlausel entspricht, ohne tatsächlich etwas zu tun, wenn Duplikate vorhanden sind.

Alex Martelli
quelle
1
Ich kann kein Unique (Benutzer, Element) einrichten, da es Instanzen geben kann, in denen mehrere Instanzen mit demselben Benutzer und Element vorhanden sind ... nur nicht, wenn ich diese Einfügung durchführe.
Der Unbekannte
2

Falls Sie keine eindeutige Einschränkung festlegen möchten, funktioniert dies wie ein Zauber:

INSERT INTO `table` (`column1`, `column2`) SELECT 'value1', 'value2' FROM `table` WHERE `column1` = 'value1' AND `column2` = 'value2' HAVING COUNT(`column1`) = 0

Ich hoffe es hilft !

Gew
quelle
2

Was du willst ist INSERT INTO table (...) SELECT ... WHERE .... aus dem MySQL 5.6 Handbuch .

In Ihrem Fall ist es:

INSERT INTO x_table (instance, user, item) SELECT 919191, 123, 456 
WHERE (SELECT COUNT(*) FROM x_table WHERE user=123 AND item=456) = 0

Oder weil Sie keine komplizierte Logik verwenden, um zu bestimmen, ob die ausgeführt werden soll INSERToder nicht, können Sie einfach einen UNIQUESchlüssel für die Kombination dieser beiden Spalten festlegen und dann verwenden INSERT IGNORE.

Martin
quelle
Hast du deine Antwort gegeben? Ich ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where...bekomme immer (läuft 5.6.34)
hlin117
Ich denke, Sie müssen from dualvorher sagen where.
hlin117
@ hlin117 In MariaDB FROM DUAList die Standardeinstellung, wenn keine Tabellen referenziert wurden ( siehe diese Dokumentation ).
Yeti
1

Wenn Sie eine Einschränkung hinzufügen, die (x_table.user, x_table.item) eindeutig ist, schlägt das Einfügen einer weiteren Zeile mit demselben Benutzer und Element fehl.

z.B:

mysql> create table x_table ( instance integer primary key auto_increment, user integer, item integer, unique (user, item));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2),(3,4);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into x_table (user, item) values (1,6);
Query OK, 1 row affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 2
NickZoic
quelle
Das ist nicht richtig. Es können mehrere Zeilen mit demselben Benutzer vorhanden sein, jedoch nicht mehrere Zeilen mit einem Benutzerelementpaar.
Matthew Flaschen
Ja Ja. Ich habe es falsch verstanden. Bearbeitet, um die Einschränkung für das Paar festzulegen.
NickZoic
Netter Kommentar ... Ich führe die Abfrage in PHP mit mysql_query ("INSERT") aus und lasse das oder die Teil, daher warnt es mich nicht vor dem Fehler, damit ich keine Überprüfungen durchführen muss
Angel.King.47
1

Obwohl es gut ist, vor dem Einfügen Ihrer Daten nach Duplikaten zu suchen, empfehle ich Ihnen, Ihren Spalten eine eindeutige Einschränkung / einen eindeutigen Index zuzuweisen, damit keine doppelten Daten versehentlich eingefügt werden können.

Beatles1692
quelle
1

Sie können die folgende Lösung verwenden, um Ihr Problem zu lösen:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE 123 NOT IN (SELECT user FROM x_table)
Keith Becker
quelle
Während dieser Code die Frage beantworten kann, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum er das Problem löst, den langfristigen Wert der Antwort verbessern.
Nic3500
0

Bei geringfügigen Änderungen an Alex 'Antwort können Sie auch einfach auf den vorhandenen Spaltenwert verweisen:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=user
Danny
quelle
0

Dieser steht also für PostgreSQL

INSERT INTO x_table
SELECT NewRow.*
FROM (SELECT 919191 as instance, 123 as user, 456 as item) AS NewRow
LEFT JOIN x_table
ON x_table.user = NewRow.user AND x_table.item = NewRow.item
WHERE x_table.instance IS NULL
xsubira
quelle
-1
Insert into x_table (instance, user, item) values (919191, 123, 456) 
    where ((select count(*) from x_table where user=123 and item=456) = 0);

Die Syntax kann je nach Datenbank variieren ...

Rick J.
quelle
2
Wofür ist das? MySQL?
Umair A.