Inserimento condizionale MySQL


98

Sto avendo difficoltà a formare un INSERT condizionale

Ho x_table con colonne (istanza, utente, elemento) in cui l'ID istanza è univoco. Voglio inserire una nuova riga solo se l'utente non ha già un dato elemento.

Ad esempio, cercando di inserire instance = 919191 user = 123 item = 456

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

Qualsiasi aiuto o guida nella giusta direzione sarebbe molto apprezzato.

Risposte:


115

Se il tuo DBMS non impone limitazioni su quale tabella selezioni quando esegui un inserimento, prova:

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)

In questo, dualc'è una tabella con una sola riga (trovata originariamente in Oracle, ora anche in mysql). La logica è che l'istruzione SELECT genera una singola riga di dati con i valori richiesti, ma solo quando i valori non sono già stati trovati.

In alternativa, guarda l'istruzione MERGE.


1
Ha dualbisogno di essere creato prima mano, in questo caso o è una sorta di speciale tabella "temporanea", creato dalla query per l'utilizzo della sola domanda?
itsmequinn

8
Se non l'hai dualgià fatto, devi crearlo. 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

Grazie che è fantastico sapere
itsmequinn

11
Nota che, in MySQL, non hai davvero bisogno di avere una tabella chiamata "dual" per esistere: è un nome di tabella speciale che può essere usato per selezionare qualsiasi cosa da essa.
Christopher Schultz

3
MySQL "rispetta" il concetto di "dual" ma in realtà non esiste. Ad esempio, se SELECT COUNT(*) FROM dualottieni sempre 1e se SELECT 'foo' FROM dualottieni sempre foo. Ma non puoi SELECT * FROM duale non puoi DESCRIBE dualo niente del genere. Non ho controllato, ma non penso nemmeno che tu possa revocare le autorizzazioni dual. Quindi vale la pena sottolineare che funziona come ti aspetti ... tranne quando non lo fa;)
Christopher Schultz

52

Puoi anche usare INSERT IGNOREche ignora silenziosamente l'inserimento invece di aggiornare o inserire una riga quando hai un indice univoco su (utente, elemento).

La query sarà simile a questa:

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

Puoi aggiungere l'indice univoco con CREATE UNIQUE INDEX user_item ON x_table (user, item).


2
Secondo la documentazione di MySQL INSERT IGNORE è la controparte di REPLACE ... INSERT IGNORE: mantiene le vecchie righe. SOSTITUISCI: mantiene le nuove righe. Entrambi operano su chiavi univoche.
Paul Kenjora,

La migliore risposta di sempre
jmojico

30

Hai mai provato qualcosa del genere?

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;

Oh si ! soluzione intelligente e piacevole con una sola istruzione SELECT
java acm

Bello. Questo funziona per postgresql al primo tentativo, dove la risposta accettata non mi sembrava.
mightypile

10

Con a UNIQUE(user, item), fai:

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

il user=123bit è un "no-op" per abbinare la sintassi della ON DUPLICATEclausola senza effettivamente fare nulla quando ci sono duplicati.


1
Non riesco a impostare un unico (utente, elemento) perché possono esserci istanze in cui più istanze con lo stesso utente e elemento ... ma non quando sto facendo questo inserto.
The Unknown,

3

Nel caso in cui non desideri impostare un vincolo univoco, funziona come un fascino:

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

Spero che sia d'aiuto !


È stato perfetto, grazie!
AndyGaskell

2

Quello che vuoi è INSERT INTO table (...) SELECT ... WHERE .... dal manuale MySQL 5.6 .

Nel tuo caso è:

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

O forse dal momento che non stai usando alcuna logica complicata per determinare se eseguire INSERTo meno, potresti semplicemente impostare una UNIQUEchiave sulla combinazione di queste due colonne e quindi utilizzare INSERT IGNORE.


Hai eseguito la tua risposta? Continuo a ricevere 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...(in esecuzione 5.6.34)
hlin117

Penso che tu debba dirlo from dualprima where.
hlin117

@ hlin117 In MariaDB FROM DUALè l'impostazione predefinita se non è stato fatto riferimento a nessuna tabella ( vedere questa documentazione ).
Yeti

1

Se aggiungi un vincolo che (x_table.user, x_table.item) è univoco, l'inserimento di un'altra riga con lo stesso utente e elemento fallirà.

per esempio:

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

Non è giusto. Possono esserci più righe con lo stesso utente, ma non più righe con una coppia utente-elemento.
Matthew Flaschen,

Si si. L'ho letto male. Modificato per rendere il vincolo sulla coppia.
NickZoic

Bel commento ... eseguo la query in PHP usando mysql_query ("INSERT") e lascio la parte o die quindi mi avverte dell'errore quindi non devo fare alcun controllo
Angel.King.47

1

Sebbene sia bene verificare la duplicazione prima di inserire i dati, suggerisco di mettere un vincolo / indice univoco sulle colonne in modo che nessun dato duplicato possa essere inserito per errore.


1

È possibile utilizzare la seguente soluzione per risolvere il problema:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE 123 NOT IN (SELECT user FROM x_table)

Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo su come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Nic3500

0

Leggera modifica alla risposta di Alex, potresti anche fare riferimento al valore della colonna esistente:

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

0

Quindi questo sta per 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

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

La sintassi può variare a seconda del DB ...


2
A cosa serve? MySQL?
Umair A.
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.