MySQL當批量插入趕上惟一索引

1、 背景

之前使用SQL Server進行表分區的時候就碰到不少關於惟一索引的問題:Step8:SQL Server 當表分區趕上惟一約束,沒想到在MySQL的分區中同樣會遇到這樣的問題:MySQL表分區實戰html

今天咱們來了解MySQL惟一索引的一些知識:包括如何建立,如何批量插入,還有一些技巧上SQL; 性能

這些問題的根源在什麼地方?有什麼共同點?MySQL中也有分區對齊的概念?惟一索引是在不少系統中都會出現的要求,有什麼辦法能夠避免?它對性能的影響有多大? 測試

2、過程 spa

(一) 導入差別數據,忽略重複數據,IGNORE INTO的使用 3d

在MySQL建立表的時候,咱們一般建立一個表的時候是以一個自增ID值做爲主鍵,那麼MySQL就會以PRIMARY KEY做爲彙集索引鍵和主鍵,既然是主鍵,那固然是惟一的了,因此重複執行下面的插入語句會報1062錯誤:如Figure1所示; code

複製代碼
-- 建立測試表 CREATE TABLE `testtable` ( `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserId` INT(11) DEFAULT NULL, `UserName` VARCHAR(10) DEFAULT NULL, `UserType` INT(11) DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 插入測試數據 INSERT INTO testtable(Id,UserId,UserName,UserType) VALUES(1,101,'aa',1),(2,102,'bbb',2),(3,103,'ccc',3);
複製代碼

u1_1062

(Figure1:Duplicate entry '1' for key 'PRIMARY') htm

可是在實際的生產環境中,需求每每是須要在UserId鍵值中設置惟一索引,今天我就以這個做爲示例,進行惟一索引的測試: blog

複製代碼
-- 建立測試表1 CREATE TABLE `testtable1` ( `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserId` INT(11) DEFAULT NULL, `UserName` VARCHAR(10) DEFAULT NULL, `UserType` INT(11) DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `IX_UserId` (`UserId`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 建立測試表2 CREATE TABLE `testtable2` ( `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserId` INT(11) DEFAULT NULL, `UserName` VARCHAR(10) DEFAULT NULL, `UserType` INT(11) DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `IX_UserId` (`UserId`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 插入測試數據1 INSERT INTO testtable1(Id,UserId,UserName,UserType) VALUES(1,101,'aa',1),(2,102,'bbb',2),(3,103,'ccc',3); -- 插入測試數據2 INSERT INTO testtable2(Id,UserId,UserName,UserType) VALUES(1,201,'aaa',1),(2,202,'bbb',2),(3,203,'ccc',3),(4,101,'xxxx',5);
複製代碼

u2_table1

(Figure2:testtable1記錄) 索引

u3_table2

(Figure3:testtable2記錄) 事務

經過執行上面的SQL腳本,咱們在testtable1和testtable2都建立了惟一索引:UNIQUE KEY `IX_UserId` (`UserId`),這就說明UserId在testtable1和testtable2表中都是惟一的,若是把testtable2的數據批量導入到 testtable1,若是執行下面【導入1】的SQL,就會出現1062的錯誤,致使整個過程會回滾,沒有達到導入差別數據的目的。

-- 導入1 INSERT INTO testtable1(UserId,UserName,UserType) SELECT UserId,UserName,UserType FROM testtable2;

u4_unique

(Figure4:Duplicate entry '101' for key 'IX_UserId')

MySQL提供一個關鍵字:IGNORE,這個關鍵字判斷每條記錄是否存在,是否違反餓了表中的惟一索引,若是存在就不插入,而不存在的記錄就會插入。

-- 導入2 INSERT IGNORE INTO testtable1(UserId,UserName,UserType) SELECT UserId,UserName,UserType FROM testtable2;

因此執行完【導入2】,就會產生Figure5的結果,這已經達到了咱們的目的了,可是你有沒發現自增的ID值跳過了一些值,這是由於咱們以前執行 【導入1】失敗形成的,雖然咱們的事務回滾了,可是自增ID會出現斷層。在SQL Server中也會有這樣的問題。擴展閱讀:簡單實用SQL腳本Part:查找SQL Server 自增ID值不連續記錄

u5_效果

Figure5:IGNORE效果)

(二) 導入並覆蓋重複數據,REPLACE INTO 的使用

1. 把testtable1和testtable2分別回滾到Figure2和Figure3的狀態(使用TRUNCATE TABLE命名再執行Insert語句),這個時候再執行下面的SQL,看有什麼效果:

-- 導入3 REPLACE INTO testtable1(UserId,UserName) SELECT UserId,UserName FROM testtable2;

u6_rep

(Figure6:REPLACE效果)

從上圖Figure6中,咱們能夠看到:UserId爲101的記錄發生了改變,不單UserName修改了,並且UserType也變爲NULL了。

因此,若是導入中發現了重複的,先刪除再插入,若是記錄有多個字段,在插入的時候若是有的字段沒有賦值,那麼新插入的記錄這些字段爲空(新插入記錄的UserType都爲NULL)。

須要注意的是,當你replace的時候,若是被插入的表若是沒有指定列,會用NULL表示,而不是這個表原來的內容。若是插入的內容列和被插入的表列同樣,則不會出現NULL。

2. 若是咱們表結構UserType字段不容許爲空,並且沒有默認值的狀況,執行【導入3】會發生什麼事情呢?

u7_not null

(Figure7:返回警告信息)

u8_0

(Figure8:UserType被設置爲0)

經過Figure7和Figure8,咱們知道數據記錄仍是插入了,只是返回Field 'UserType' doesn't have a default value的警告,插入記錄的UserType字段都被設置爲0('UserType' 爲int數據類型)。

3. 若是咱們但願導入的時候一塊兒更新UserType字段的值,這天然很簡單了,使用下面的SQL腳本就能夠解決:

-- 導入4 REPLACE INTO testtable1(UserId,UserName,UserType) SELECT UserId,UserName,UserType FROM testtable2;

u9_rep

(Figure9:一塊兒更新UserType)

(三) 導入保留重複數據未指定字段,INSERT INTO ON DUPLICATE KEY UPDATE 的使用

把testtable1和testtable2分別回滾到Figure2和Figure3的狀態(使用TRUNCATE TABLE命名再執行Insert語句),這個時候再執行下面的SQL,看有什麼效果:

-- 導入5 INSERT INTO testtable1(UserId,UserName) SELECT UserId,UserName FROM testtable2 ON DUPLICATE KEY UPDATE testtable1.UserName = testtable2.UserName;

u10_update

(Figure10:保留UserType值)

對比Figure二、Figure3與Figure10UserId爲101的記錄:更新了UserName的值,保留了UserType的值;可是因爲【導入5】中沒有指定UserType,因此新插入記錄的UserType是爲NULL的。

-- 導入6 INSERT INTO testtable1(UserId,UserName,UserType) SELECT UserId,UserName,UserType FROM testtable2 ON DUPLICATE KEY UPDATE testtable1.UserName = testtable2.UserName;

u11_update

(Figure11:保留UserType值)

對比Figure二、Figure3與Figure11,只插入testtable2表的UserId,UserName字段,可是保留 testtable1表的UserType字段。若是發現有重複的記錄,作更新操做;在原有記錄基礎上,更新指定字段內容,其它字段內容保留。

(四) 總結

當在一個UNIQUE鍵上插入包含重複值的記錄時,默認的insert會報1062錯誤,MYSQL能夠經過以上三種不一樣的方式和你的業務邏輯進行處理。

相關文章
相關標籤/搜索