2009-06-25 12 views
12

Sto tentando di inserire una nuova riga, ma se la chiave esiste già, voglio aggiornare SOLO la riga se un altro valore nella tabella è diverso. È possibile ciò in una query/istruzione mysql?subordinato all'aggiornamento della chiave duplicata

Il mio tavolo è composto dalle seguenti colonne: cappello, guanti, nome, last_update

cappello + guanti compongono l'indice univoco (dicono i valori di "cappello" e "guanti" sono i colori)

Supponiamo che questo sia già nella tabella:

 
1. hat=blue mittens=green name=george last_update=tuesday 
2. hat=red mittens=green name=bill last_update=monday 

Su una nuova chiave, voglio inserire come al solito. Sulla chiave duplicata, voglio fare un aggiornamento SOLO SE il nome cambia, altrimenti ignoro. La ragione di ciò è che voglio conservare il valore last_update (timestamp).

 
hat=yellow mittens=purple name=jimmy -- insert new row 
hat=blue mittens=green name=george -- ignore 
hat=blue mittens=green name=betty -- update row 

Questo è possibile senza l'utilizzo di dichiarazioni separate per guardare prima la riga esistente, confrontare i valori e quindi rilasciare un aggiornamento, se necessario? Se sì, quale sarebbe la sintassi?


Grazie per le vostre risposte. Ho provato tutti loro. Infatti, utilizzando solo una semplice dichiarazione UPDATE come

update tbl set name='george' where hat='blue' and mittens='green' 

risultati in alcuna riga in fase di aggiornamento. Ma, utilizzando

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 

o

INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 

traduce in qualche modo nella fila essendo aggiornato (e il timestamp modificato).

FWIW, questo è il tavolo che sto usando:

CREATE TABLE `tbl` (
`hat` varchar(11) default NULL, 
`mittens` varchar(11) default NULL, 
`name` varchar(11) default NULL, 
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
UNIQUE KEY `clothes` (`hat`,`mittens`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

MySQL è la versione 4.1.22 (forse questo importa?) Ancora una volta, il mio apprezzamento per tutte le risposte.

+0

direi che è la vostra versione di MySQL che è il problema qui. Sembra che abbiano corretto un bug (http://bugs.mysql.com/bug.php?id=28904) con INSERT ... ON DUPLICATE KEY UPDATE nel ramo 5.0, dopo il supporto attivo per il ramo 4.1 terminato (Dec 31, 2006). Ho ricreato la tua tabella e il test case sulla mia macchina e ha funzionato come previsto, e sto utilizzando MySQL Server 5.0.67. Se non riesci ad aggiornare l'installazione di MySQL, potresti rimanere bloccato senza una soluzione "singola query" al tuo problema. – zombat

+0

CONFERMATO. Entrambe le soluzioni funzionano su 5.0.45 (sfortunatamente, è solo un server di sviluppo). Grazie ancora! – javalina

+0

Hai provato la seconda delle due domande che ho suggerito?Quello che controlla manualmente cosa è successo alla colonna timbro (last_update)? Credo che uno dovrebbe funzionare anche se quel bug esiste in quanto decide esplicitamente se aggiornare la colonna o meno con un'altra dichiarazione del caso. – Dipin

risposta

17

È possibile utilizzare i normali costrutti sql nella sintassi ON DUPLICATE KEY. Quindi, al fine di fare gli aggiornamenti condizionali durante un inserto è possibile effettuare le seguenti operazioni:

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END; 

Questo cambierà il valore di ciò che hai fornito per l'istruzione INSERT quando è diverso da ciò che è in prima fila e si impostare il valore a essere quello che è già se non è cambiato e risulterà in MySQL che non sta facendo nulla alla riga preservando il timestamp last_update come ha fatto notare Quassnoi.

Se si voleva fare sicuro al 100% che non fa affidamento sul comportamento di MySQL in cui non si aggiorna una riga se si imposta un valore a se stesso è possibile effettuare le seguenti operazioni per forzare il timestamp:

INSERT INTO tbl (hat, mittens, name) 
VALUES ('yellow','purple','jimmy') 
ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) 
            THEN VALUES(name) ELSE name END 
         , last_update = CASE WHEN name <> VALUES(name) 
             THEN now() ELSE last_update END; 

Questo sarà solo aggiornare il last_update-now() quando il nome è cambiato altrimenti vi dirà MySQL per mantenere il valore di last_update.

Inoltre, nella sezione ON DUPLICATE KEY dell'istruzione è possibile fare riferimento alle colonne nella tabella in base al loro nome ed è possibile ottenere i valori forniti alla sezione dei valori dell'istruzione insert utilizzando la funzione VALUES(column_name).


Il seguente è un registro che mostra che l'ultima dichiarazione fornita funziona anche sulla 4.1 dove gli altri non funzionano a causa di un bug che è stato risolto nella versione 5.0.

C:\mysql\bin>mysql -u root -p 
Enter password: 
Welcome to the MySQL monitor. Commands end with ; or \g. 
Your MySQL connection id is 1 to server version: 4.1.22-community 

Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 

mysql> show databases; 
+----------+ 
| Database | 
+----------+ 
| mysql | 
| test  | 
+----------+ 
2 rows in set (0.00 sec) 

mysql> use test; 
Database changed 
mysql> show tables; 
Empty set (0.00 sec) 

mysql> CREATE TABLE `tbl` (
    -> `hat` varchar(11) default NULL, 
    -> `mittens` varchar(11) default NULL, 
    -> `name` varchar(11) default NULL, 
    -> `stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 
    -> UNIQUE KEY `clothes` (`hat`,`mittens`) 
    ->) ENGINE=MyISAM DEFAULT CHARSET=latin1; 
Query OK, 0 rows affected (0.01 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george'); 
Query OK, 1 row affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:16 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name='george'; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:30 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat, mittens, name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name=CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') ON DUPLICATE KEY UPDATE name = CASE WHEN name <> VALUES(name) THEN VALUES(name) ELSE name END, stamp = CASE WHEN name <> VALUES(name) THEN now() ELSE stamp END; 
Query OK, 2 rows affected (0.00 sec) 

mysql> select * from tbl; 
+------+---------+--------+---------------------+ 
| hat | mittens | name | stamp    | 
+------+---------+--------+---------------------+ 
| blue | green | george | 2009-06-27 12:15:42 | 
+------+---------+--------+---------------------+ 
1 row in set (0.00 sec) 

mysql> 

Fatemi sapere se avete domande.

HTH,

-Dipin

+3

Per me è stato un po 'difficile scoprire cosa <> sta per (non uguale a!) Quindi ho pensato di aggiungere questo qui in un commento nel caso in cui qualcun altro si chiedesse in futuro. :) – Alisso

+0

Ciao piccolo aiuto come posso interrogare quando ho più colonne durante l'aggiornamento – Chaitanya

0

Non è necessario fare nulla, questo è il comportamento predefinito.

Se la query seleziona una riga per UPDATE ma il valore aggiornato rimane la stessa, come in:

UPDATE table 
SET  col = col 

, il timestamp rimane anche la stessa.

Questa riga non viene conteggiata come interessata, la query restituirà 0 rows affected.

3

È necessaria la sintassi INSERT ... ON DUPLICATE KEY UPDATE.

Vostri criteri sarebbe simile a questa:

INSERT INTO tbl (hat,mittens,name) VALUES ('blue','green','george') 
    ON DUPLICATE KEY UPDATE name='george'; 

Se si ha un record con blu/verde/george per il cappello/guanti/nome già, senza UPDATE sarebbe in realtà essere eseguita, e la vostra data e ora non sarebbe aggiornato. Se tuttavia avessi un record con blue/green/betty, allora "betty" verrebbe sovrascritto con "george" e il tuo timestamp verrebbe aggiornato.

0

Se si sta eseguendo un multiplo INSERT (sia da SELECT o fornire più righe a VALUES), è possibile effettuare le seguenti operazioni:

INSERT INTO tbl (hat,mittens,name) VALUES 
    ('yellow','purple','jimmy'), 
    ('blue','green','george'), 
    ('blue','green','betty') 
ON DUPLICATE KEY UPDATE name = VALUES(name); 

Questo sarà:

  • Inserisci righe che aren' t duplica w/resp. agli indici UNIQUE
  • Aggiornare la colonna name per gli altri (quando il nuovo valore è diverso)

Caveat: i commenti nel documentation on the ON DUPLICATE syntax dice che il risultato mysql_affected_rows() poi non è affidabile.

Problemi correlati