2009-02-14 19 views
688

Durante l'esecuzione di un'istruzione INSERT con più righe, desidero saltare le voci duplicate che altrimenti causerebbero un errore. Dopo alcune ricerche, le mie opzioni sembrano essere l'uso di uno:"INSERT IGNORE" vs "INSERT ... ON DUPLICATE KEY UPDATE"

  • ON DUPLICATE KEY UPDATE che implica un aggiornamento inutile ad un certo costo, o
  • INSERT IGNORE che implica un invito per altri tipi di fallimento a scivolare in senza preavviso.

Ho ragione in queste ipotesi? Qual è il modo migliore per saltare semplicemente le righe che potrebbero causare duplicati e continuare semplicemente sulle altre righe?

risposta

858

Si consiglia di utilizzare INSERT...ON DUPLICATE KEY UPDATE.

Se si utilizza INSERT IGNORE, la riga non verrà effettivamente inserita se risulta in una chiave duplicata. Ma la dichiarazione non genererà un errore. Invece genera un avvertimento. Questi casi includono:

  • inserimento di una chiave duplicata in colonne con PRIMARY KEY o UNIQUE vincoli.
  • Inserimento di un NULL in una colonna con un vincolo NOT NULL.
  • Inserimento di una riga in una tabella partizionata, ma i valori inseriti non vengono associati a una partizione.

Se si utilizza REPLACE, MySQL in realtà fa un DELETE seguito da un INSERT internamente, che ha alcuni effetti collaterali imprevisti:

  • un nuovo ID incremento automatico è allocato.
  • Le righe dipendenti con chiavi esterne possono essere eliminate (se si utilizzano chiavi esterne a cascata) oppure si impedisce il REPLACE.
  • I trigger che si attivano su DELETE vengono eseguiti inutilmente.
  • Gli effetti collaterali vengono propagati anche agli slave di replica.

correzione: sia REPLACE e INSERT...ON DUPLICATE KEY UPDATE sono non standard, le invenzioni di proprietà specifiche a MySQL. ANSI SQL 2003 definisce una dichiarazione MERGE che può risolvere lo stesso bisogno (e altro), ma MySQL non supporta l'istruzione MERGE.


Un utente ha tentato di modificare questo post (la modifica è stata rifiutata dai moderatori). La modifica ha tentato di aggiungere un reclamo per cui INSERT...ON DUPLICATE KEY UPDATE provoca l'assegnazione di un nuovo ID di incremento automatico. È vero che il nuovo ID è generato, ma non viene utilizzato nella riga modificata.

Vedere la dimostrazione di seguito, testata con Percona Server 5.5.28. La variabile di configurazione innodb_autoinc_lock_mode=1 (impostazione predefinita):

mysql> create table foo (id serial primary key, u int, unique key (u)); 
mysql> insert into foo (u) values (10); 
mysql> select * from foo; 
+----+------+ 
| id | u | 
+----+------+ 
| 1 | 10 | 
+----+------+ 

mysql> show create table foo\G 
CREATE TABLE `foo` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `u` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `u` (`u`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 

mysql> insert into foo (u) values (10) on duplicate key update u = 20; 
mysql> select * from foo; 
+----+------+ 
| id | u | 
+----+------+ 
| 1 | 20 | 
+----+------+ 

mysql> show create table foo\G 
CREATE TABLE `foo` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `u` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `u` (`u`) 
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 

Quanto sopra dimostra che la dichiarazione IODKU rileva il duplicato, e invoca l'aggiornamento per modificare il valore di u. Nota: AUTO_INCREMENT=3 indica che un ID è stato generato, ma non utilizzato nella riga.

Mentre REPLACE fa eliminare la riga originale e inserisce una nuova riga, generando e la memorizzazione di un nuovo ID incremento automatico:

mysql> select * from foo; 
+----+------+ 
| id | u | 
+----+------+ 
| 1 | 20 | 
+----+------+ 
mysql> replace into foo (u) values (20); 
mysql> select * from foo; 
+----+------+ 
| id | u | 
+----+------+ 
| 3 | 20 | 
+----+------+ 
+0

Bello vado sugli effetti collaterali! Grazie. Non mi dispiace che le righe duplicate non vengano inserite. Questo è in realtà ciò che voglio. Che altro viene ignorato da INSERT IGNORE? –

+0

insert ignore credo ignora gli errori – IEnumerator

+0

Trasforma gli errori in avvisi. Ho aggiunto alcuni dettagli sopra, per i casi di errori che vengono ignorati quando si utilizza INSERISCI IGNORA. Potrebbero esserci anche altri casi. –

5

Replace In sembra un'opzione. Oppure puoi verificare con

IF NOT EXISTS(QUERY) Then INSERT 

Questo inserirà o eliminerà quindi inserirà. Tendo prima a cercare il IF NOT EXISTS.

+0

Grazie per la rapida risposta. Presumo dappertutto, ma presumo che questo sarebbe simile a ON DUPLICATE KEY UPDATE in quanto eseguirà un aggiornamento non necessario. Sembra dispendioso, ma non ne sono sicuro. Ognuno di questi dovrebbe funzionare. Mi chiedo se qualcuno sa qual è il migliore. –

+4

NTuplip: questa soluzione è ancora aperta alle condizioni di gara dagli inserimenti da transazioni concorrenti. –

+0

'REPLACE' cancella tutte le righe nella tabella con la corrispondenza di _any_' PRIMARY' o 'UNIQUE', _then_' INSERTs'.Questo è potenzialmente molto più lavoro di IODKU. –

13

Io abitualmente uso INSERT IGNORE, e suona come esattamente il tipo di comportamento stai cercando anche tu. Finché saprai che le righe che causerebbero conflitti di indice non saranno inserite e pianifichi il tuo programma di conseguenza, non dovrebbe causare alcun problema.

+3

Sono preoccupato che ignorerò gli errori altri della duplicazione. È corretto o INSERT IGNORE ignora solo ignora solo l'errore di duplicazione? Grazie! –

+2

Trasforma qualsiasi errore in un avviso. Vedi una lista di questi casi nella mia risposta. –

+0

Questo è un peccato; Vorrei che ignorasse solo i duplicati fallimenti. –

7

ON DUPLICATE KEY UPDATE non è nello standard. È come standard come lo è REPLACE. Vedi SQL MERGE.

In sostanza, entrambi i comandi sono versioni di sintassi alternativa dei comandi standard.

+1

replace esegue una cancellazione e inserisce, mentre l'aggiornamento della chiave onduplicate aggiorna la riga esistente. alcune differenze sono: ID auto incrementale, posizione riga, un gruppo di trigger – ahnbizcad

15

So che questo è vecchio, ma aggiungerò questa nota nel caso in cui qualcun altro (come me) arrivi a questa pagina durante il tentativo di trovare informazioni su INSERT..IGNORE.

Come accennato in precedenza, se si utilizza INSERT..IGNORE, gli errori che si verificano durante l'esecuzione dell'istruzione INSERT vengono invece considerati come avvertimenti.

Una cosa che non è esplicitamente menzionata è che INSERIRE ..IGNORE causerà valori non validi verranno adeguati ai valori più vicini quando inseriti (mentre i valori non validi potrebbero causare l'interruzione della query se la parola chiave IGNORE non è stata utilizzata).

+6

Non sono veramente sicuro di cosa intendi per "valori non validi" e corretto per cosa? Potresti fornire un esempio o un'ulteriore spiegazione? – Marenz

+4

Significa che se si inserisce un tipo di dati errato in un campo quando si utilizza "INSERISCI IGNORA", i dati verranno modificati per corrispondere al tipo di dati del campo e verrà inserito un valore potenzialmente non valido, quindi la query continuerà a essere eseguita. Con "INSERT" solo, verrebbe generato un errore sul tipo di dati errato e la query verrebbe annullata.Ciò potrebbe essere corretto con un numero inserito in un campo varchar o di testo, ma l'inserimento di una stringa di testo in un campo con un tipo di dati numerico comporterebbe dati errati. – codewaggle

+2

@Marenz un altro esempio: se la tua tabella ha una colonna non null e la tua query "INSERT IGNORE" non specifica un valore per quella colonna, la riga verrà inserita con un valore zero in quella colonna indipendentemente dal fatto che strict sql_mode sia abilitato. – Shannon

36

Qualcosa di importante da aggiungere: quando si utilizza INSERISCI IGNORE e si verificano violazioni delle chiavi, MySQL NON genera un avviso!

Se si tenta ad esempio di inserire 100 record alla volta, con uno uno difettoso, si otterrebbe in modalità interattiva:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

come si vede : Nessun avvertimento! Questo comportamento è anche descritto erroneamente nella documentazione ufficiale di Mysql.

Se il tuo script deve essere informato, se alcuni record non sono stati aggiunti (a causa di violazioni delle chiavi) devi chiamare mysql_info() e analizzarlo per il valore "Duplicati".

+4

Se stai usando PHP, dovrai usare ['mysqli_affected_rows()'] (http://php.net/manual/es/mysqli.affected-rows.php) per sapere se il 'INSERT' effettivamente è accaduto. –

+0

Con MySQL 5.5 e MariaDB 10 I _do_ ottiene un errore 'Impossibile aggiungere o aggiornare una riga secondaria: un vincolo di chiave esterna non riesce '_e non vengono aggiunte righe_ (anche valide). – Floris

+2

@Floris Questo errore è dovuto a un * vincolo di chiave esterna * e non dovuto a una * chiave duplicata *. Sto usando MySQL 5.5.28. Quando si utilizza 'INSERT IGNORE', le chiavi duplicate vengono ignorate senza errori o avvisi. – toxalot

147

Nel caso in cui si vuole vedere che cosa questo tutti i mezzi, ecco un blow-by-blow di tutto:

CREATE TABLE `users_partners` (
    `uid` int(11) NOT NULL DEFAULT '0', 
    `pid` int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`uid`,`pid`), 
    KEY `partner_user` (`pid`,`uid`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 

chiave primaria si basa su entrambe le colonne di questa tabella di riferimento rapido. Una chiave primaria richiede valori univoci.

Cominciamo:

INSERT INTO users_partners (uid,pid) VALUES (1,1); 
...1 row(s) affected 

INSERT INTO users_partners (uid,pid) VALUES (1,1); 
...Error Code : 1062 
...Duplicate entry '1-1' for key 'PRIMARY' 

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1); 
...0 row(s) affected 

INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid 
...0 row(s) affected 

nota, il sopra salvato troppo lavoro extra impostando la colonna uguale a se stesso, nessun aggiornamento effettivamente necessario

REPLACE INTO users_partners (uid,pid) VALUES (1,1) 
...2 row(s) affected 

e ora alcune prove multiple righe:

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) 
...Error Code : 1062 
...Duplicate entry '1-1' for key 'PRIMARY' 

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) 
...3 row(s) affected 

nessun altro messaggio è stato generato in console e ora ha quei 4 valori in i dati della tabella. Ho cancellato tutto eccetto (1,1) in modo da poter testare dallo stesso campo di gioco

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid 
...3 row(s) affected 

REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) 
...5 row(s) affected 

Così ce l'hai. Dato che questo era tutto eseguito su un tavolo nuovo con quasi nessun dato e non in produzione, i tempi per l'esecuzione erano microscopici e irrilevanti. Chiunque disponga di dati del mondo reale sarebbe più che benvenuto a contribuire.

+0

Ho eseguito entrambe le chiavi duplicate e sostituito. Le mie tabelle sono terminate con ~ 120K righe con circa il 30% delle mie righe duplicate. La chiave duplicata ha funzionato in 102 secondi e la sostituzione ha funzionato in 105 secondi. Per il mio caso, mi sto attenendo alla chiave duplicata. – crunkchitis

+0

Testato quanto sopra con MariaDB 10 e ricevuto un avviso durante l'esecuzione di 'INSERISCI IGNORE INTO users_partners (uid, pid) VALUES (1,1), (1,2), (1,3), (1,4)'. – Floris

+0

Quale versione di MySQL hai usato per tutto questo? –

0

Se si utilizza con una dichiarazione SHOW WARNINGS; alla fine del set di query verrà visualizzata una tabella con tutti gli avvisi, inclusi gli ID dei duplicati.

1

Potenziale pericolo di INSERIMENTO IGNORA. Se si sta tentando di inserire un valore VARCHAR più lungo, la colonna è stata definita con - il valore sarà troncato e inserito EVEN SE è abilitata la modalità strict.

0

Se si desidera inserire nella tabella e sul conflitto della chiave primaria o dell'indice univoco aggiornerà la riga in conflitto anziché inserire quella riga.

Sintassi:

inserto in serie tabla1 column1 = a, b = column2 su dulplicate aggiornamento COLUMN2 = c;

Ora questa istruzione di inserimento può apparire diversa da ciò che si è visto in precedenza. Questa istruzione di inserimento tenta di inserire una riga in table1 con il valore di a e b nella colonna column1 e column2 rispettivamente.

Cerchiamo di capire questa affermazione in una profonda:

Ad esempio: - qui column1 è definita come la chiave primaria in tabella 1.

Ora se nella tabella 1 non è presente alcuna riga con il valore "a" in column1. Quindi questa affermazione inserirà una riga nella tabella1.

Ora se nella tabella 1 è presente una riga con il valore "a" nella colonna 2. Quindi questa affermazione aggiornerà il valore column2 della riga con "c" dove il valore column1 è "a".

Quindi, se si desidera inserire una nuova riga, aggiornare la riga sul conflitto della chiave primaria o dell'indice univoco. Read more on this link

Problemi correlati