mysql replace語句
語法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
REPLACE [LOW_PRIORITY | DELAYED]
[ INTO ] tbl_name
[PARTITION (partition_name,...)]
[(col_name,...)]
{ VALUES | VALUE} ({expr | DEFAULT },...),(...),...
Or :
REPLACE [LOW_PRIORITY | DELAYED]
[ INTO ] tbl_name [PARTITION (partition_name,...)]
SET col_name={expr | DEFAULT }, ...
Or :
REPLACE [LOW_PRIORITY | DELAYED]
[ INTO ] tbl_name
[PARTITION (partition_name,...)]
[(col_name,...)]
SELECT ...
|
原理
replace的工作機制有點像insert,只不過如果在表裏如果一行有PRIMARY KEY或者UNIQUE索引,那麽就會把老行刪除然後插入新行。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[email protected] 03:23:55>show create table lingluo\G
*************************** 1. row ***************************
Table : lingluo
Create Table : CREATE TABLE `lingluo` (
`a` int (11) NOT NULL DEFAULT ‘0‘ ,
`b` int (11) DEFAULT NULL ,
`c` int (11) DEFAULT NULL ,
`d` int (11) DEFAULT NULL ,
PRIMARY KEY (`a`), --------------------------同時存在PK約束
UNIQUE KEY `uk_bc` (`b`,`c`) ----------------唯一索引約束
) ENGINE=InnoDB DEFAULT CHARSET=gbk
1 row in set (0.01 sec)
[email protected] 02:01:44> select * from lingluo;
Empty set (0.00 sec)
[email protected] 03:27:40> replace into lingluo values (1,10000,3,4); --------表裏沒有已存在的記錄相當於insert
Query OK, 1 row affected (0.00 sec) -----------------------affect_rows是1
binlog格式:
|
1 2 3 |
[email protected] 02:11:18> replace into lingluo values (1,10000,3,5); -------已經存在記錄,且PK和UK同時沖突的時候,相當於先delete再insert
Query OK, 2 rows affected (0.00 sec) ----------------------affect_rows是2,是delete和insert行數的總和
binlog格式:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[email protected] 02:26:09> select * from lingluo;
+ ---+-------+------+------+
| a | b | c | d |
+ ---+-------+------+------+
| 1 | 10000 | 3 | 5 |
+ ---+-------+------+------+
1 row in set (0.00 sec)
[email protected] 02:31:54> replace into lingluo values (1,10000,4,5); -------已經存在記錄,且PK同時沖突的時候,相當於先delete再insert
Query OK, 2 rows affected (0.00 sec) ---------------------------------affect_rows是2,是delete和insert行數的總和
[email protected] 02:32:02> select * from lingluo;
+ ---+-------+------+------+
| a | b | c | d |
+ ---+-------+------+------+
| 1 | 10000 | 4 | 5 |
+ ---+-------+------+------+
binlog格式:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[email protected] 02:37:04> replace into lingluo values (4,10000,6,5);
Query OK, 1 row affected (0.00 sec)
[email protected] 02:37:59> replace into lingluo values (6,10000,6,5); -------已經存在記錄,且UK同時沖突的時候,直接update
Query OK, 2 rows affected (0.00 sec) ---------------------------------affect_rows是2
[email protected] 02:40:31> select * from lingluo;
+ ---+-------+------+------+
| a | b | c | d |
+ ---+-------+------+------+
| 1 | 10000 | 4 | 5 |
| 3 | 10000 | 5 | 5 |
| 6 | 10000 | 6 | 5 |
+ ---+-------+------+------+
3 rows in set (0.00 sec)
binlog格式:
|
疑問:
既然uk沖突的時候是update,那麽為什麽affect_rows都是2呢?讓我們從源碼上分析看下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
指定列 replace :
[email protected] 03:34:37> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 1 | 126 |
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | 2 | 129 |
+ ----+------+------+
6 rows in set (0.00 sec)
[email protected] 03:34:37> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 1 | 126 |
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | 2 | 129 |
+ ----+------+------+
6 rows in set (0.00 sec)
[email protected] 03:34:40> replace into u (age,d) values (0,130);
Query OK, 2 rows affected, 1 warning (0.01 sec)
[email protected] 03:40:39>show warnings;
+ ---------+------+-----------------------------------------+
| Level | Code | Message |
+ ---------+------+-----------------------------------------+
| Warning | 1364 | Field ‘id‘ doesn‘t have a default value |
+ ---------+------+-----------------------------------------+
1 row in set (0.00 sec)
[email protected] 03:40:47> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 0 | 130 | -----------------因為id是parimary但是沒有auto_creasement,由126變成130
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | 2 | 129 |
+ ----+------+------+
6 rows in set (0.00 sec)
|
用的時候需要註意的是:
-
如果指定replace列的話,盡量寫全,要不然沒有輸入值的列數據會被賦成默認值(因為是先delete在insert),就和普通的insert是一樣的,所以如果你要執行replace語句的話是需要insert和delete權限的。
如果你需要執行
SET
,就相當於執行col_name
=col_name
+ 1
.col_name
= DEFAULT(col_name
) + 1 -
replace語句如果不深入看的話,就和insert一樣,執行完後沒什麽反應
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
[email protected] 04:20:04> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 0 | 130 |
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | 2 | 129 |
+ ----+------+------+
6 rows in set (0.00 sec)
[email protected] 04:20:10> replace into u (id,d) values (8,232);
Query OK, 1 row affected (0.01 sec)
[email protected] 04:20:39> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 0 | 130 |
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | 2 | 129 |
| 8 | NULL | 232 |
+ ----+------+------+
7 rows in set (0.00 sec)
[email protected] 04:20:43> replace into u (id,d) values (7,232);
Query OK, 3 rows affected (0.01 sec) ----------註意這裏affect_rows是3,因為主鍵7已經存在,唯一索引232已經存在,所以需要刪除id為7和8的行,然後插入新行
[email protected] 04:20:52> select * from u;
+ ----+------+------+
| id | age | d |
+ ----+------+------+
| 0 | 0 | 130 |
| 1 | 0 | 1 |
| 3 | 1 | 123 |
| 4 | 1 | 127 |
| 5 | 0 | 12 |
| 7 | NULL | 232 |
+ ----+------+------+
6 rows in set (0.00 sec)
[email protected] 04:20:55>
|
MySQL給replace和load data....replace用的算法是:
-
嘗試向表裏插入新行
-
當表裏唯一索引或者primary key沖突的時候:
a. delete沖突行
b.往表裏再次插入新行
如果遇到重復行沖突,存儲過程很可能當作update執行,而不是delete+insert,但是顯式上都是一樣的。這裏沒有用戶可見的影響除了存儲引擎層Handler_xxx
的狀態變量。
因為REPLACE ... SELECT語句的結果依賴於select的行的順序,但是順序沒辦法保證都是一樣的,有可能從master和slave的都不一樣。正是基於這個原因,MySQL 5.6.4以後,REPLACE ... SELECT語句被標記為基於statement的復制模式不安全的。基於這個變化,當使用STATEMENT記錄二進制日誌的時候,如果有這樣的語句就會在log裏面輸出一個告警,同樣當使用MIXED行復制模式也會記錄告警。
在MySQL5.6.6之前的版本,replace影響分區表就像MyISAM使用表級鎖鎖住所有的分區表一樣。當使用 REPLACE ... PARTITION
語句時確實會發生上述情況。(使用基於行鎖的InnoDB引起不會發生這種情況。)在MySQL 5.6.6以後的版本MySQL使用分區鎖,只有當分區(只要沒有分區表的列更新)包含了REPLACE語句並且WHERE實際匹配到的才會鎖住那個分區;否則的話就會鎖住整個表。
操作形式:
binlog格式:
結論
-
當存在pk沖突的時候是先delete再insert
-
當存在uk沖突的時候是直接update
mysql replace語句