2009-06-25 140 views
12

我試圖插入一個新的行,但如果該鍵已經存在,我想只更新行,如果表中的某個其他值是不同的。這可能在MySQL查詢/語句?條件重複鍵更新

我的表包括以下欄:帽子,手套,名稱,LAST_UPDATE

帽子+手套彌補了唯一索引(說的「帽子」和「手套」的值是顏色)

我們假設這已經在表中:

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

在一個新的密鑰上,我想插入像往常一樣。在重複密鑰上,我想只在名稱更改時進行更新,否則忽略。這是因爲我想保留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 

這是可能的,而不使用單獨的語句來先查找現有的行,比較值,然後在必要時發佈更新?如果是這樣,語法是什麼?


感謝您的回覆。我嘗試了所有這些。事實上,只使用一個簡單的UPDATE語句,如

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

導致沒有行被更新。但是,無論是使用

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

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; 

不知何故導致該行中被更新(和時間戳改變)。

FWIW,這是我使用的表:

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是4.1.22版本(也許這事?) 同樣,我對所有的答覆表示感謝。

+0

我猜想,這是你的MySQL版本,這是問題。看起來他們修復了一個bug(http://bugs.mysql.com/bug.php?id=28904)與5.0分支中的INSERT ... ON DUPLICATE KEY UPDATE,4.1分支的主動支持結束後(Dec 31,2006)。我在我的機器上重新創建了表和測試用例,並且它按預期工作,並且正在運行MySQL Server 5.0.67。如果你不能更新你的MySQL安裝,那麼你可能會陷入沒有「單一查詢」解決方案的問題。 – zombat 2009-06-25 19:28:10

+0

確認。這兩種解決方案都適用於5.0.45(不幸的是,這只是一個開發服務器)。再次感謝! – javalina 2009-06-25 20:21:51

回答

17

您可以在ON DUPLICATE KEY語法中使用普通的sql結構。因此,爲了在插入時做有條件的更新,你可以做到以下幾點:

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; 

這將值更改爲您提供的插入語句是什麼時,它從什麼是行不同,將其值設置爲如果它沒有改變,那麼它將成爲它的原樣,並且會導致MySQL沒有對Quassnoi指出的保留last_update時間戳的行做任何事情。

如果你想使100%肯定,你是不是靠的MySQL在它不更新行的行爲,如果你值設置爲自己,你可以做以下迫使時間戳:

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; 

這將只更新last_updatenow()當名稱改變,否則它會告訴MySQL保留last_update的值。

此外,在語句的ON DUPLICATE KEY部分中,可以通過名稱引用表中的列,並且可以使用VALUES(column_name)函數獲取您提供給插入語句值部分的值。


下面是一個日誌,顯示所提供的最後一條語句適用於4.1即使別人不工作,由於這是固定在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> 

讓我知道如果您有任何問題。

HTH,

-Dipin

0

你需要什麼都不做,這是默認行爲。

如果你的查詢選擇UPDATE行,但更新後的值保持不變,如:

UPDATE table 
SET  col = col 

,時間戳也保持不變。

該行甚至沒有算作受影響,查詢將返回0 rows affected

3

您需要INSERT ... ON DUPLICATE KEY UPDATE語法。

您的查詢應該是這樣的:

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

如果您有相關的記錄藍/綠/喬治的帽子/手套/名稱已經,沒有更新將實際執行,以及您的時間戳不會更新。如果你有藍/綠/貝蒂的記錄,那麼'貝蒂'將被'喬治'覆蓋,你的時間戳會被更新。

0

如果你(通過SELECTVALUES提供多行)執行多INSERT,你可以做到以下幾點:

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

這將:

是AREN
  • 插入行」 t重複w/resp。獨特的指標
  • 更新爲他人name列(當新的值是不同的)

警告:documentation on the ON DUPLICATE syntax評論說,mysql_affected_rows()後來的結果是不可靠的。