先描述一下這個問題的原由,假設有一張表,裏面保存了交易訂單,每張訂單有惟一的ID,有最後更新時間,還有數據,詳情以下:
sql
+-------+----------+------+-----+---------------------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------------------+-------+ | UID | int(11) | NO | PRI | 0 | | | Time | datetime | NO | | 0000-00-00 00:00:00 | | | Data | int(11) | YES | | NULL | | +-------+----------+------+-----+---------------------+-------+
針對這張表會作追加及更新的操做,具體來講就是若是訂單不存在就INSERT一條新的,若是已存在就UPDATE。因爲入庫前沒法得知相應記錄是否已存在,一般的作法沒法如下幾種:數據庫
一、先SELECT一下,再決定INSERT仍是UPDATE;ide
二、直接UPDATE,若是受影響行數是0,再INSERT;測試
三、直接INSERT,若是發生主鍵衝突,再UPDATE;spa
這幾種方法都有缺陷,對MySQL來講其實最好的是直接利用INSERT...ON DUPLICATE KEY UPDATE...語句,具體到上面的test表,執行語句以下 :索引
INSERT INTO test VALUES (1, '2016-1-1', 10) ON DUPLICATE KEY UPDATE Time='2016-1-1',Data=10;
能夠很好的插入或更新數據,一條語句就搞定,至此一直工做得很好。文檔
後來由於查詢方式變動,要求將UID和Time兩個字段作聯合主鍵,此時表結構以下:it
+-------+----------+------+-----+---------------------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------------------+-------+ | UID | int(11) | NO | PRI | 0 | | | Time | datetime | NO | PRI | 0000-00-00 00:00:00 | | | Data | int(11) | YES | | NULL | | +-------+----------+------+-----+---------------------+-------+
可是問題來了:一但Time字段被更新,即便是相同的UID,也被數據庫認爲是不一樣的主鍵,所以不會產生主鍵衝突,上面的語句就失效了,數據庫裏出現了不少UID相同的數據。
class
開始尋找解決辦法,其實也簡單,按MySQL文檔裏的說明,ON DUPLICATE KEY UPDATE語句判斷是否衝突是依靠主鍵或惟一索引,所以爲UID創建惟一索引就能夠了。先建索引:test
CREATE UNIQUE INDEX IDX_UID ON test(UID);
再測試一下插入:
INSERT INTO test VALUES (1, '2016-1-1', 10) ON DUPLICATE KEY UPDATE Time='2016-1-1',Data=10; INSERT INTO test VALUES (1, '2016-2-1', 20) ON DUPLICATE KEY UPDATE Time='2016-2-1',Data=20;
檢查數據庫,能夠看到不會有多條數據生成,惟一的一條數據是Data字段被更新成20的,成功。