MySQL: Datensatz einfügen, falls nicht in der Tabelle vorhanden

384

Ich versuche die folgende Abfrage auszuführen:

INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name='value'
);

Dies gibt jedoch einen Fehler zurück. Grundsätzlich möchte ich keinen Datensatz einfügen, wenn das Feld 'Name' des Datensatzes bereits in einem anderen Datensatz vorhanden ist. Wie kann ich überprüfen, ob der neue Name eindeutig ist?

Rupert
quelle
12
Alle aktuellen Antworten auf diese oder Dupes setzen voraus, dass Sie einen eindeutigen Index hinzufügen können. Manchmal basiert die Entscheidung auf einer Geschäftslogik, die nicht auf den gesamten Tisch übertragen werden kann. Sie lassen beispielsweise mehrere Zeilen mit einem bestimmten Wert in einer Spalte zu, ein anderer Wert in der Spalte darf jedoch nur in einer Zeile angezeigt werden. Wie erreichen wir das?
Oscar

Antworten:

502

Ich schlage nicht vor, dass Sie dies tun, da der UNIQUEvon Piskvor und anderen vorgeschlagene Index ein weitaus besserer Weg ist, dies zu tun, aber Sie können tatsächlich das tun, was Sie versucht haben:

CREATE TABLE `table_listnames` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `address` varchar(255) NOT NULL,
  `tele` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

Datensatz einfügen:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

Versuchen Sie erneut, denselben Datensatz einzufügen:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

Fügen Sie einen anderen Datensatz ein:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
|  2 | John   | Doe       | 022  |
+----+--------+-----------+------+

Und so weiter...


Aktualisieren:

Um #1060 - Duplicate column nameFehler zu vermeiden , falls zwei Werte gleich sind, müssen Sie die Spalten des inneren SELECT benennen:

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+---------+-----------+------+
| id | name    | address   | tele |
+----+---------+-----------+------+
|  1 | Rupert  | Somewhere | 022  |
|  2 | John    | Doe       | 022  |
|  3 | Unknown | Unknown   | 022  |
+----+---------+-----------+------+
Mike
quelle
18
Danke, das hat geholfen. Mein eigentliches Problem ist weitaus komplexer und die Spalte kann einfach nicht eindeutig sein und ich kann mich nicht auf den Primärschlüssel verlassen. Aber genau das habe ich gesucht.
Rupert
2
@ Piskovar: Einverstanden. @ Rupert: Sie sollten die Spalte, auf die in der inneren select-Anweisung verwiesen wird ( namein diesem Fall), nach Möglichkeit indizieren . Beachten Sie auch, dass Sie dies SELECT 'John', 'Doe', '022' FROM table_listnamesanstelle von SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- tun können, dies funktioniert jedoch nur, wenn table_listnamesbereits eine oder mehrere Zeilen enthalten sind. Ich bezweifle, dass die Geschwindigkeit anders ist, also ist es wahrscheinlich kein Problem.
Mike
3
@VijayRamamurthy: Dies funktioniert, weil Sie das Ergebnis einer Select-Anweisung einfügen. Lesen Sie die Abfrage sorgfältig durch -> Die WHEREAnweisung gehört zur SELECTAbfrage. Die Select-Abfrage gibt entweder eine einzelne
Philipp
13
Dies scheint nicht zu funktionieren, wenn Sie denselben Wert zweimal in verschiedene Felder einfügen möchten (z. B. SELECT 'Humbert', 'Humbert', '1'in der inneren Auswahl). Ich erhalteERROR 1060 (42S21): Duplicate column name
cburgmer
28
@cburgmer Ich habe das gleiche Problem #1060 - Duplicate column name. Haben Sie eine Lösung gefunden? Edit: Gefunden. Fügen Sie hinter jedem Wert ein AS hinzu:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack
292

INSERT erlaubt keine WHERESyntax .

Was Sie tun können: Erstellen Sie ein UNIQUE INDEXFeld, das eindeutig sein sollte ( name), und verwenden Sie dann entweder:

  • normal INSERT(und behandeln Sie den Fehler, wenn der Name bereits existiert)
  • INSERT IGNORE(was im Stillen fehlschlägt und eine Warnung (anstelle eines Fehlers) verursacht, wenn der Name bereits vorhanden ist)
  • INSERT ... ON DUPLICATE KEY UPDATE(wird UPDATEam Ende ausgeführt, wenn der Name bereits vorhanden ist, siehe Dokumentation )
Piskvor verließ das Gebäude
quelle
1
wenn Sie Warnmeldung erhalten möchten , können Sie danach tunmysql> show warnings\G
fiorentinoing
2
Ime, die Warnmeldung im Fall von INSERT IGNOREentspricht dem ^Duplicate entry '.+' for key '.+'$
regulären Ausdruck
1
Wenn Sie Insert Ignorieren verwenden, werden andere Fehler ignoriert, die möglicherweise nicht auf Duplikate zurückzuführen sind?
gepex
1
@gepex Ja, das wird es. Das ist ein großes Problem bei der Verwendung von INSERT IGNOREIMO.
Shmosel
1
Hinweis: Diese Antwort ist möglicherweise nicht die beste Wahl, wenn eine der Spalten in der eindeutigen Schlüsselbeschränkung nullbar ist! Die Einschränkung funktioniert, aber Sie können andere Ergebnisse erwarten :) Das Einfügen ("John Doe", NULL), ("John Doe", NULL) führt zu zwei Zeilen, obwohl beide Felder zusammen eine eindeutige Schlüsseleinschränkung aufweisen. Siehe auch stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol
57

Hat funktioniert :

INSERT INTO users (full_name, login, password) 
  SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS 
  (SELECT login FROM users WHERE login='tito');
Mahbub Tito
quelle
Dies funktioniert nur für Oracle-Datenbanken, oder? en.wikipedia.org/wiki/DUAL_table Diese Frage bezieht sich speziell auf MySQL!
Sehr unregelmäßig
1
Ich teste nicht in der Oracle-Datenbank. Es funktioniert gut bei MySQL und wurde getestet.
Mahbub Tito
9
Ich habe seitdem erfahren, dass "dual" in MySQL verfügbar ist, aber nicht in MariaDB, dem Open-Source-Zweig von MySQL
Highly Irregular
3
Aus dem Wikipedia-Link von @ HighlyIrregular "Mehrere andere Datenbanken (einschließlich Microsoft SQL Server, MySQL, PostgreSQL, SQLite und Teradata) ermöglichen es, die FROM-Klausel vollständig wegzulassen, wenn keine Tabelle benötigt wird. Dadurch wird die Notwendigkeit einer Dummy-Tabelle vermieden." Die gleiche Abfrage scheint in MySql (5.7, falls dies wichtig ist) ohne FROM DUAL zu funktionieren.
Stannius
1
Getestet und funktioniert gut in MariaDB! Vielen Dank, dass Sie @ MahbubTito
Jeff Monteiro
27

MySQL bietet eine sehr nette Lösung:

REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));

Sehr einfach zu bedienen, da Sie einen eindeutigen Primärschlüssel deklariert haben (hier mit Wert 5).

sdesvergez
quelle
Um dies zu verhindern, müssen Sie einen eindeutigen Index erstellen. Weitere Informationen finden Sie hier: Link
Azmeer
Sehr gefährlich. Versuchen Sie es nicht, es sei denn, Sie wissen, was Sie tun. Versuchen Sie es eigentlich gar nicht. Überprüfen Sie andere Antworten.
Jhourlad Estrella
3
Beachten Sie, dass dies alle übereinstimmenden Zeilen löscht und sie dann erneut einfügt. Eine sicherere Version desselben Verhaltens ist die Verwendung von ON DUPLICTE KEY UPDATE, mit der mathematische Zeilen aktualisiert werden, anstatt sie zu löschen und erneut einzufügen
Omn
22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Hier die MySQL-Abfrage, die Datensätze einfügt, wenn sie nicht vorhanden sind, und vorhandene ähnliche Datensätze ignoriert.

----Untested----
Montaser El-sawy
quelle
4
es hat bei mir nicht funktioniert, INSERT IGNORE IN emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
Pitu
8
Sieht aus wie eine Mischung aus Einfüge- und Aktualisierungssyntax. Meinst du INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo
@Hobo Von MySQL Handbuch dev.mysql.com/doc/refman/5.7/en/insert.html INSERT - Syntax: Es kann sein , INSERT [...] IGNORE INTO mytable VALUES ...oderINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam
"Wenn Sie den Modifikator IGNORE verwenden, werden Fehler, die beim Ausführen der INSERT-Anweisung auftreten, ignoriert." Sie haben also im Grunde nichts gelöst.
Marcelo Agimóvel
18

Sie können einfach den folgenden Weg verwenden:

INSERT INTO ... ON DUPLICATE KEY UPDATE ...

Auf diese Weise können Sie neue Rohdaten einfügen und bei doppelten Daten eine bestimmte Spalte ersetzen (die besten Spalten sind Zeitstempel).
Beispielsweise :

CREATE TABLE IF NOT EXISTS Devices (
  id         INT(6)       NOT NULL AUTO_INCREMENT,
  unique_id  VARCHAR(100) NOT NULL PRIMARY KEY,
  created_at VARCHAR(100) NOT NULL,
  UNIQUE KEY unique_id (unique_id),
  UNIQUE KEY id (id)
)
  CHARACTER SET utf8
  COLLATE utf8_unicode_ci;

INSERT INTO Devices(unique_id, time) 
VALUES('$device_id', '$current_time') 
ON DUPLICATE KEY UPDATE time = '$current_time';
Arash Hatami
quelle
13

Wenn Sie wirklich keinen eindeutigen Index für die Tabelle erhalten können, können Sie versuchen ...

INSERT INTO table_listnames (name, address, tele)
    SELECT 'Rupert', 'Somewhere', '022'
        FROM some_other_table
        WHERE NOT EXISTS (SELECT name
                              FROM table_listnames
                              WHERE name='Rupert')
        LIMIT 1;
Brian Hooper
quelle
12

Um ein ähnliches Problem zu lösen, habe ich die Tabelle so geändert, dass sie eine eindeutige Spalte enthält. Anhand Ihres Beispiels hätte ich bei der Erstellung Folgendes:

name VARCHAR(20),
UNIQUE (name)

Verwenden Sie dann beim Einfügen die folgende Abfrage:

INSERT IGNORE INTO train
set table_listnames='Rupert'
obsessiveCookie
quelle
Ich bevorzuge diese Lösung.
Ilyas Karim
9

Diese Abfrage funktioniert gut:

INSERT INTO `user` ( `username` , `password` )
    SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
    WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks' 
    AND `password` = md5( 'Nepal' )) LIMIT 1

Und Sie können die Tabelle mit folgender Abfrage erstellen:

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`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Hinweis: Erstellen Sie eine Tabelle mit der zweiten Abfrage, bevor Sie versuchen, die erste Abfrage zu verwenden.

Dakab
quelle
md5 Passwort Hash .... yikes!
Chad Grant
4

Brian Hooper: Sie haben fast den Punkt erreicht, aber Sie haben einen Fehler in Ihrer Synatx. Ihre Beilage wird niemals funktionieren. Ich habe in meiner Datenbank getestet und hier ist die richtige Antwort:

INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
            SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
                FROM dual
                WHERE NOT EXISTS (SELECT datum,ura
                                      FROM podatki
                                      WHERE datum='$datum' and ura='$ura'

Ich gebe Ihnen mein Beispiel für einen Tisch. Das Einfügen ist fast das gleiche wie das, was Bian Hooper geschrieben hat, außer dass ich die Auswahl FROM DUAL aus einer anderen Tabelle übernommen habe. Cind Grüße, Ivan

Ivan Laharnar
quelle
2
Was ist das "dual"? Ein eingebautes Schlüsselwort? Ich sehe nur keinen Unterschied zu dem, was Brian gesagt hat ... BEARBEITEN: Weitere Untersuchungen wurden gemischt und widersprachen (?) Ergebnissen. Während die SQL DUAL-Tabellenseite besagt, dass MySQL keine DUAL-Tabellen unterstützt, sagt das MySQL-Handbuch , dass dies der Fall ist. Mein eigener Test zeigt, dass dies nicht der Fall ist, da unknown table status: TABLE_TYPENachrichten angezeigt werden, obwohl die Abfrage ein Ergebnis lieferte. Wahrscheinlich, weil MySQL die FROM DUALKlausel nicht benötigt ?
not2qubit
4
insert into customer_keyskill(customerID, keySkillID)
select  2,1 from dual
where not exists ( 
    select  customerID  from customer_keyskill 
    where customerID = 2 
    and keySkillID = 1 )
Oms G.
quelle
Dual ist eine Oracle-Tabelle, nicht MySQL
Highly Irregular
3

Sie fügen das Ergebnis nicht ein. Sie können die Namensspalte in der Primärspalte definieren oder festlegen, dass sie eindeutig ist.

Benutzer 1034
quelle
3

Ich hatte ein Problem und die von Mike empfohlene Methode funktionierte teilweise. Ich hatte den Fehler Dublicate Column name = '0' und änderte die Syntax Ihrer Abfrage wie folgt

     $tQ = "INSERT  INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5) 
                SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5' 
FROM DUAL
                WHERE NOT EXISTS (
                SELECT sum FROM names WHERE name_id = '$name' 
AND surname_id = '$surname') LIMIT 1;";

Das Problem war mit Spaltennamen. sum3 war gleich sum4 und mysql warf republikanische Spaltennamen, und ich schrieb den Code in dieser Syntax und es funktionierte perfekt.

Hovhannes Babayan
quelle
1
Der Eigentümer von DUAL ist SYS (SYS besitzt das Datenwörterbuch, daher ist DUAL Teil des Datenwörterbuchs.) Auf DUAL kann jedoch jeder Benutzer zugreifen. Die Tabelle enthält eine einzelne VARCHAR2 (1) -Spalte namens DUMMY mit dem Wert 'X'. Mit MySQL kann DUAL als Tabelle in Abfragen angegeben werden, für die keine Daten aus Tabellen erforderlich sind. Die DUAL-Tabelle ist eine spezielle einzeilige, einspaltige Tabelle, die standardmäßig in Oracle- und anderen Datenbankinstallationen vorhanden ist. In Oracle enthält die Tabelle eine einzelne VARCHAR2 (1) -Spalte namens DUMMY mit dem Wert 'X'. Es eignet sich zur Auswahl einer Pseudospalte wie SYSDATE oder USER.
Adnan Haider
3

Ich hatte ein ähnliches Problem und musste mehrere einfügen, wenn es nicht vorhanden war. Aus den obigen Beispielen bin ich zu dieser Kombination gekommen ... sie ist hier, nur für den Fall, dass jemand sie brauchen würde.

Hinweis: Ich musste den Namen überall definieren, da MSSQL ihn benötigte ... MySQL funktioniert auch mit *.

INSERT INTO names (name)
SELECT name
FROM
(
  SELECT name
  FROM
  (
     SELECT 'Test 4' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 4'
  )
  UNION ALL
  SELECT name
  FROM
  (
     SELECT 'Test 5' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 5'
  )
) tmp_all;

MySQL: CREATE TABLE names( OIDint (11) NICHT NULL AUTO_INCREMENT, namevarchar (32) COLLATE utf8_unicode_ci NICHT NULL, PRIMARY KEY ( OID), EINZIGARTIGER SCHLÜSSEL name_UNIQUE( name)) ENGINE = InnoDB AUTO_INCREMENT = 1;

oder

MSSQL: CREATE TABLE [Namen] ([OID] INT IDENTITY (1, 1) NICHT NULL, [Name] NVARCHAR (32) NICHT NULL, PRIMARY KEY CLUSTERED ([OID] ASC)); CREATE UNIQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names] ([name] ASC);

Waldemar
quelle
2

Diese Abfrage kann in PHP-Code verwendet werden.

Ich habe eine ID-Spalte in dieser Tabelle, daher muss ich für alle Spalten außer dieser ID-Spalte auf Duplizierung prüfen:

#need to change values
SET @goodsType = 1, @sybType=5, @deviceId = asdf12345SDFasdf2345;    


INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT @goodsType, @sybType, @deviceId) AS tmp
WHERE NOT EXISTS (
    SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
    WHERE `goodsTypeId` = @goodsType
        AND `goodsId` = @sybType
        AND `deviceId` = @deviceId
) LIMIT 1;

und jetzt wird ein neues Element nur hinzugefügt, wenn keine Zeile mit in der SETZeichenfolge konfigurierten Werten vorhanden ist

Andrew
quelle