MySQL: inserisci il record se non esiste nella tabella


384

Sto cercando di eseguire la seguente query:

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

Ma questo restituisce un errore. Fondamentalmente non voglio inserire un record se il campo "nome" del record esiste già in un altro record - come verificare se il nuovo nome è univoco?



12
Tutte le risposte correnti a questo o ai duplicati presuppongono che sia possibile aggiungere un indice univoco. A volte la decisione si basa su una logica aziendale che non può essere imposta all'intera tabella. Ad esempio, consenti a più righe con un determinato valore in una colonna, ma un altro valore nella colonna potrà apparire solo su una riga. Come lo realizziamo?
Oscar

Risposte:


502

In realtà non sto suggerendo di farlo, poiché l' UNIQUEindice come suggerito da Piskvor e altri è un modo molto migliore per farlo, ma puoi effettivamente fare quello che stavi tentando:

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;

Inserisci un record:

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  |
+----+--------+-----------+------+

Prova a inserire di nuovo lo stesso record:

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  |
+----+--------+-----------+------+

Inserisci un altro record:

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  |
+----+--------+-----------+------+

E così via...


Aggiornare:

Per evitare #1060 - Duplicate column nameerrori nel caso in cui due valori possano essere uguali, è necessario assegnare un nome alle colonne del SELECT interno:

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  |
+----+---------+-----------+------+

18
Grazie che mi ha aiutato. Il mio problema reale è molto più complesso e la colonna non può essere unica e non posso dipendere dalla chiave primaria. Ma questo è esattamente quello che stavo cercando.
Rupert,

2
@Piskovar: concordato. @Rupert: dovresti indicizzare la colonna a cui fai riferimento nell'istruzione select interna ( name, in questo caso), se possibile. Nota anche che puoi farlo SELECT 'John', 'Doe', '022' FROM table_listnames, invece di SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- ma funzionerà solo se table_listnamescontiene già una o più righe. Dubito che la velocità sia comunque diversa, quindi probabilmente non è un problema.
Mike

3
@VijayRamamurthy: funziona perché inserisci il risultato di un'istruzione Select. Leggere attentamente la query -> L' WHEREistruzione appartiene alla SELECTquery. La query Select restituisce una singola riga di dati (i dati sono inseriti) o nessun dato (non viene inserito nulla)
Philipp

13
Questo non sembra funzionare se si desidera inserire due volte lo stesso valore in campi diversi (ad es. SELECT 'Humbert', 'Humbert', '1'Nella selezione interna). Ricevo unERROR 1060 (42S21): Duplicate column name
cburgmer il

28
@cburgmer Ho avuto lo stesso problema #1060 - Duplicate column name. Hai trovato una soluzione? Modifica: trovato. Aggiungi dietro ogni valore un AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack,

292

INSERT non consente WHEREnella sintassi .

Cosa puoi fare: crea un UNIQUE INDEXcampo che dovrebbe essere unico ( name), quindi usa:

  • normale INSERT(e gestire l'errore se il nome esiste già)
  • INSERT IGNORE(che fallirà silenziosamente causerà un avviso (anziché errore) se il nome esiste già)
  • INSERT ... ON DUPLICATE KEY UPDATE(che eseguirà UPDATEalla fine se il nome esiste già, consultare la documentazione )

1
se hai bisogno di ricevere un messaggio di avvertimento puoi farlo in seguitomysql> show warnings\G
fiorentino il

2
ime, il messaggio di avviso, nel caso di INSERT IGNORE, corrisponde alla regex di^Duplicate entry '.+' for key '.+'$
user2426679

1
se si utilizza insert ignore, ignorerà altri errori che potrebbero verificarsi che non sono dovuti a duplicati?
gepex,

1
@gepex Sì, lo farà. Questo è un grosso problema con l'utilizzo INSERT IGNOREdell'IMO.
shmosel,

1
Nota: questa risposta potrebbe non essere la scelta migliore se una delle colonne nel vincolo chiave univoca è nullable! Il vincolo funziona, ma potresti aspettarti altri risultati :) L'inserimento ("John Doe", NULL), ("John Doe", NULL) comporta due righe sebbene entrambi i campi insieme abbiano un vincolo chiave univoco. Vedere anche stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol

57

Lavorato :

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');

Funziona solo con i database Oracle, giusto? en.wikipedia.org/wiki/DUAL_table Questa domanda riguarda specificamente MySQL!
Altamente irregolare,

1
Non eseguo test nel database Oracle. Funziona bene su MySQL e testato.
Mahbub Tito,

9
Da allora ho imparato che "dual" è disponibile in MySQL ma non in MariaDB, il ramo open source di MySQL
Highly Irregular

3
Dal link wikipedia di @ HighlyIrregular "Diversi altri database (inclusi Microsoft SQL Server, MySQL, PostgreSQL, SQLite e Teradata) consentono di omettere completamente la clausola FROM se non è necessaria alcuna tabella. Ciò evita la necessità di qualsiasi tabella fittizia." La stessa query sembra funzionare in MySql (5.7, se questo è importante) senza FROM DUAL.
stannius

1
Testato e funzionante bene in MariaDB! Grazie @MahbubTito
Jeff Monteiro,

27

MySQL offre una soluzione molto carina:

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

Molto facile da usare poiché hai dichiarato una chiave primaria univoca (qui con valore 5).


per
evitarlo

Molto pericoloso. Non tentare se non sai cosa stai facendo. In realtà, non tentare affatto. Controlla altre risposte.
Jhourlad Estrella,

3
Nota: questo ELIMINA tutte le righe corrispondenti e quindi le reinserisce, una versione più sicura di questo stesso comportamento è quella di utilizzare ON DUPLICTE KEY UPDATE, che aggiorna le righe matematiche anziché eliminarle e reinserirle: dev.mysql.com/doc/ refman / 5.7 / it / replace.html
Omn,

22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Qui la query mysql, che inserisce i record se non esistono e ignorerà i record simili esistenti.

----Untested----

4
non ha funzionato per me, INSERISCI IGNORA IN emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
pitu,

8
Sembra una combinazione di sintassi di inserimento e aggiornamento. Intendi INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo,

@Hobo Dal manuale di MySQL dev.mysql.com/doc/refman/5.7/it/insert.html Sintassi INSERT: Può essere INSERT [...] IGNORE INTO mytable VALUES ...OppureINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam,

"Se si utilizza il modificatore IGNORE, gli errori che si verificano durante l'esecuzione dell'istruzione INSERT vengono ignorati.". Quindi sostanzialmente non hai risolto nulla.
Marcelo Agimóvel,

18

Puoi facilmente usare il seguente modo:

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

In questo modo è possibile inserire qualsiasi nuovo raw e se si dispone di dati duplicati, sostituire la colonna specifica (la colonna migliore è data / ora).
Per esempio :

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';

13

Se davvero non riesci a ottenere un indice univoco sul tavolo, puoi provare ...

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;

12

Per ovviare a un problema simile, ho modificato la tabella per avere una colonna unica. Usando il tuo esempio, al momento della creazione avrei qualcosa di simile:

name VARCHAR(20),
UNIQUE (name)

e quindi utilizzare la seguente query durante l'inserimento in essa:

INSERT IGNORE INTO train
set table_listnames='Rupert'

Preferisco questa soluzione.
Ilyas karim,

9

Questa query funziona bene:

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

E puoi creare la tabella usando la seguente query:

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 ;

Nota: creare una tabella utilizzando la seconda query prima di provare a utilizzare la prima query.


hash password md5 .... yikes!
Chad Grant,

4

Brian Hooper: Hai quasi raggiunto il punto ma hai un errore nella tua sintassi. Il tuo inserto non funzionerà mai. Ho testato sul mio database ed ecco la risposta giusta:

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'

Ti sto dando il mio esempio di y table. L'inserimento è quasi lo stesso di quello scritto da Bian Hooper, tranne per il fatto che ho inserito il comando FROM DUAL su un'altra tabella. Cordiali saluti, Ivan


2
Cos'è quel "doppio"? Una parola chiave integrata? Semplicemente non vedo la differenza da quello che Brian ha detto ... EDIT: Ulteriori indagini hanno ottenuto risultati contrastanti e contraddittori (?). Mentre la pagina della tabella DUAL di SQL afferma che MySQL non supporta le tabelle DUAL, il manuale di MySQL lo afferma. Il mio test mostra che non lo fa, poiché fornisce unknown table status: TABLE_TYPEmessaggi, sebbene la query abbia prodotto un risultato. Probabilmente perché MySQL non richiede la FROM DUALclausola?
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 )

dual è una tabella Oracle non MySQL
Altamente irregolare,

3

Non stai inserendo Aggiornamento del risultato. È possibile definire la colonna del nome nella colonna principale o impostarla come unica.


3

Ho avuto un problema e il metodo consigliato da Mike ha funzionato in parte, ho riscontrato un errore Nome colonna pubblica = '0' e ho cambiato la sintassi della tua query in questo modo`

     $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;";

Il problema era con i nomi delle colonne. sum3 era uguale a sum4 e mysql ha lanciato nomi di colonne pubblici e ho scritto il codice in questa sintassi e ha funzionato perfettamente,


1
Il proprietario di DUAL è SYS (SYS possiede il dizionario dei dati, quindi DUAL fa parte del dizionario dei dati.) Ma DUAL è accessibile a qualsiasi utente. La tabella ha una singola colonna VARCHAR2 (1) chiamata DUMMY che ha un valore di 'X'. MySQL consente di specificare DUAL come tabella nelle query che non richiedono dati da alcuna tabella. La tabella DUAL è una tabella speciale a una riga e una colonna presente per impostazione predefinita in Oracle e altre installazioni di database. In Oracle, la tabella ha una singola colonna VARCHAR2 (1) chiamata DUMMY che ha un valore di 'X'. È adatto per l'uso nella selezione di una pseudo colonna come SYSDATE o USER.
Adnan haider

3

Ho avuto un problema simile e ho dovuto inserire più se non esistenti. Quindi dagli esempi sopra sono arrivato a questa combinazione ... è qui nel caso qualcuno ne avesse bisogno.

Avviso: ho dovuto definire il nome ovunque come richiesto da MSSQL ... Anche MySQL funziona con *.

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) NOT NULL AUTO_INCREMENT, namevarchar (32) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY ( OID), UNIQUE KEY name_UNIQUE(name )) ENGINE = InnoDB AUTO_INCREMENT = 1;

o

MSSQL: CREATE TABLE [names] ([OID] INT IDENTITY (1, 1) NOT NULL, [name] NVARCHAR (32) NOT NULL, PRIMARY KEY CLUSTERED ([OID] ASC)); CREA UN INDICE UNICO NON ESCLUSITO [Index_Names_Name] ON [names] ([name] ASC);


2

Questa query può essere utilizzata nel codice PHP.

Ho una colonna ID in questa tabella, quindi ho bisogno di controllare la duplicazione per tutte le colonne tranne questa colonna ID:

#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;

e ora il nuovo elemento verrà aggiunto solo nel caso in cui non esistano righe con valori configurati in SETstringa

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.