向數據庫插入記錄時,有時會有這種需求,當符合某種條件的數據存在時,去修改它,不存在時,則新增,也就是saveOrUpdate操作。這種控制可以放在業務層,也可以放在數據庫層,大多數數據庫都支持這種需求,如Oracle的merge語句,再如本文所講的MySQL中的INSERT ... ON DUPLICATE KEY UPDATE語句。
該語句是基於唯一索引或主鍵使用,比如一個字段a被加上了unique index,並且表中已經存在了一條記錄值為1,下面兩個語句會有相同的效果:
- INSERT INTO table (a,b,c) VALUES (1,2,3)
- ON DUPLICATE KEY UPDATE c=c+1;
- UPDATE table SET c=c+1 WHERE a=1;
ON DUPLICATE KEY UPDATE后面可以放多個字段,用英文逗號分割。使用ON DUPLICATE KEY UPDATE,最終如果插入了一個新行,則受影響的行數是1,如果修改了已存在的一行數據,則受影響的行數是2。
如果字段b也被加上了unique index,則該語句和下面的update語句是等效的:
- UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;
如果a=1 OR b=2匹配了多行,則只有一行會被修改。通常的,在ON DUPLICATE KEY UPDATE語句中,我們應該避免多個唯一索引的情況。如果需要插入或更新多條數據,並且更新的字段需要根據其它字段來運算時,可以使用如下語句:
- INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
- ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
在ON DUPLICATE KEY UPDATE后面使用VALUES()方法,這個語句等同於下面的兩個語句:
- INSERT INTO table (a,b,c) VALUES (1,2,3)
- ON DUPLICATE KEY UPDATE c=3;--1+2
- INSERT INTO table (a,b,c) VALUES (4,5,6)
- ON DUPLICATE KEY UPDATE c=9;--4+5
如果一個表中包含了一個auto_increment的字段,每次insert數據后,可以通過last_insert_id()方法返回最后自動生成的值,如果通過INSERT ... ON DUPLICATE KEY UPDATE語句修改了一條數據,那么再通過last_insert_id()方法獲取的值將不正確,實際測試中是多了一個數,比如向表中增加了3條數據,那么通過last_insert_id()方法得到的值是3,但是通過該語句修改了一條數據后,通過last_insert_id()方法得到的值是4。如果想解決該問題,可以通過如下語句:
- INSERT INTO table (a,b,c) VALUES (1,2,3)
- ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
重點是這句id=LAST_INSERT_ID(id)。
英文原文:https://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
方案一:使用ignore關鍵字
如果是用主鍵primary或者唯一索引unique區分了記錄的唯一性,避免重復插入記錄可以使用:
INSERT IGNORE INTO `table_name` (`email`, `phone`, `user_id`) VALUES ('test9@163.com', '99999', '9999');
這樣當有重復記錄就會忽略,執行后返回數字0
還有個應用就是復制表,避免重復記錄:
INSERT IGNORE INTO `table_1` (`name`) SELECT `name` FROM `table_2`;
IN SERT INTO 新表(字段1,字段2,.......) SELECT 字段1,字段2,...... FROM 舊表
mysql中primary key重復時的處理辦法
當insert進數據表, 發生唯一key(unique key與primary key)重復時, 會發生duplicate key錯誤.
這種情況有三種處理方法, 以下面的數據結構為例子
mysql> use test;
mysql> create table `user` (`userid` int(11) DEFAULT NULL, `username` varchar(255) NOT NULL DEFAULT '');
給加上userid列primary key
mysql> alter table `user` add primary key `userid` (`userid`);
插入數據
mysql> insert into `user` values (1, 'eric'), (2, 'jesus');
現在我要插入或者編輯userid為1的記錄, 但我不知道里面是否已經存在該記錄.
1, 先刪除再插入之
mysql> delete from user where userid = 1;
mysql> insert into user values (1, 'xxxxx') ;
2, 使用replace into
mysql> replace into user values (1, 'newvalue');
這種情況下邏輯是這樣的, mysql先判斷記錄是否存在, 若存在則先刪除之, 再自行insert. 所以你能看到這條語句執行后affected rows是2條(當然前提是你的數據表里userid為1的數據只有1條)
3, 使用insert into ... on duplicate key update
mysql> insert into user1 values (1, 'newvalueagain') on duplicate key update user1.username = VALUES(username);
這條語句的affected rows也是2.
當然還有另外的處理方式就是直接用php來實現,
先select出來, 發現沒結果則insert, 否則update.
還可以先update, 發現affected rows是0, 則insert.
但明顯這倆種辦法都沒有把工作直接交給mysql處理效率高 。
參考:
http://blog.csdn.net/ghsau/article/details/23557915
