MySQL中的replace語句
一、背景
當使用replace語句更新access_apps表時,原有的mark列、remark列的信息丟失。
CREATE TABLE `access_apps` (
`base` varchar (11) NOT NULL DEFAULT ‘‘ ,
`business` varchar (64) NOT NULL DEFAULT ‘‘ ,
`owt` varchar (64) NOT NULL DEFAULT ‘‘ ,
`pdl` varchar (64) NOT NULL DEFAULT ‘‘ ,
`app_group_id` varchar (64) NOT NULL DEFAULT ‘‘ ,
`app_artifact_id` varchar (64) NOT NULL DEFAULT ‘‘ ,
`app` varchar (128) NOT NULL DEFAULT ‘‘ ,
`appkey` varchar (128) NOT NULL DEFAULT ‘‘ ,
`version` varchar (128) NOT NULL DEFAULT ‘‘ ,
`status` tinyint(1) NOT NULL DEFAULT ‘0‘ ,
`mark` int (11) NOT NULL DEFAULT ‘0‘ ,
`remark` varchar (128) NOT NULL DEFAULT ‘‘ ,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY (`base`,`business`,`owt`,`pdl`,`app_group_id`,`app_artifact_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
@Insert ( "replace into access_apps(base,business,owt,pdl,app_group_id,app_artifact_id,app,appkey,version,status) " +
"values(#{base},#{business},#{owt},#{pdl},#{app_group_id},#{app_artifact_id},#{app},#{appkey},#{version},#{status})" )
void saveAccessRecord(AccessRecord accessRecord);
|
access_apps表中有mark、remark列,replace語句中沒有mark、remark列。
錯誤原因:
錯誤認為replace語句相當於當主鍵沖突時,已修改的列將使用新值,未修改的列將使用舊值
replace語句對於缺失的列將使用默認值,而不是當前行中的值,導致原有的列信息丟失。
二、replace語句詳解
MySQL中replace語句具體算法如下:
1. 嘗試把新行插入到表中
2. 當因為主鍵(PRIMARY KEY)沖突錯誤或(UNIQUE INDEX)唯一索引重復錯誤而造成插入失敗時:
a. 從表中刪除含有重復關鍵字值的沖突行
b. 再次嘗試把新行插入到表中
可以看出replace語句相當於insert操作或者delete+insert操作,因此,為了能夠使用replace語句,必須同時具備insert和delete的權限。
replace語句執行時,分以下兩種情況:
情況1:insert
當不存在主鍵沖突或唯一索引沖突,相當於insert操作
情況2:delete and insert
當存在主鍵沖突或唯一索引沖突,相當於delete操作,加insert操作
所有列的值均取自在replace語句中被指定的值,所有缺失的列的值被設置為列的默認值,這和INSERT一樣。
不能引用當前行的值,然後用於更新新行。(因為當當前行與新行發生沖突時,當前行將被刪除,也就無法被引用了!)
例如,如果使用一個形如“SET col_name = col_name + 1”的賦值,則對位於右側的列名稱的引用會被作為DEFAULT(col_name)處理。
因此,該賦值相當於SET col_name = DEFAULT(col_name) + 1。
replace語句的返回值
replace語句會返回一個數,來指示受影響的行的數目。該數是被刪除和被插入的行數的和。
1.如果對於一個replace語句,返回值為1,則一行被插入,同時沒有行被刪除。
2.如果該數大於1,則在新行被插入前,有一個或多個舊行被刪除。如果表包含多個唯一索引,並且新行與不同行的不同的唯一索引發生了重復。
示例1:test表id作為主鍵
CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data VARCHAR (64) DEFAULT NULL ,
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY (id)
);
|
mysql> REPLACE INTO test VALUES (1, ‘Old‘ , ‘2014-08-20 18:47:00‘ );
Query OK, 1 row affected (0.04 sec)
mysql> REPLACE INTO test VALUES (1, ‘New‘ , ‘2014-08-20 18:47:42‘ );
Query OK, 2 rows affected (0.04 sec)
mysql> SELECT * FROM test;
+ ----+------+---------------------+
| id | data | ts |
+ ----+------+---------------------+
| 1 | New | 2014-08-20 18:47:42 |
+ ----+------+---------------------+
1 row in set (0.00 sec)
|
第一個replace語句執行時,test表中沒有數據,沒有發生沖突,所以相當於執行了insert操作,返回值為1(1 row affected)。
第二個replace語句執行時,id=1的數據已存在,發生了主鍵沖突,所以相當於先執行了delete操作,然後執行了insert操作,返回值為2(2 rows affected)。
示例2:test表id,ts作為主鍵
CREATE TABLE test2 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data VARCHAR (64) DEFAULT NULL ,
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY (id, ts)
);
|
mysql> REPLACE INTO test2 VALUES (1, ‘Old‘ , ‘2014-08-20 18:47:00‘ );
Query OK, 1 row affected (0.05 sec)
mysql> REPLACE INTO test2 VALUES (1, ‘New‘ , ‘2014-08-20 18:47:42‘ );
Query OK, 1 row affected (0.06 sec)
mysql> SELECT * FROM test2;
+ ----+------+---------------------+
| id | data | ts |
+ ----+------+---------------------+
| 1 | Old | 2014-08-20 18:47:00 |
| 1 | New | 2014-08-20 18:47:42 |
+ ----+------+---------------------+
2 rows in set (0.00 sec)
|
因id,ts作為主鍵,replace時未發生主鍵沖突,所以均相當於insert操作。
三、參考資料
https://dev.mysql.com/doc/refman/5.7/en/replace.html MySQL官網replace語法
http://www.cnblogs.com/c-961900940/p/6197878.html MySQL中replace into的用法
http://www.cnblogs.com/martin1009/archive/2012/10/08/2714858.html mysql replace into用法詳細說明
MySQL中的replace語句