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索引,那麼就會把老行刪除而後插入新行。如:mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@test 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)
 
root@test 02:01:44> select  from  lingluo;
Empty  set  (0.00 sec)
 
root@test 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__mysql_dbconsole031221____expect_.png

1
2
3
root@test 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__mysql_dbconsole031221____expect_.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@test 02:26:09> select  from  lingluo;
+ ---+-------+------+------+
| a | b     | c    | d    |
+ ---+-------+------+------+
| 1 | 10000 |    3 |    5 |
+ ---+-------+------+------+
1 row  in  set  (0.00 sec)
 
root@test 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行數的總和
 
root@test 02:32:02> select  from  lingluo;
+ ---+-------+------+------+
| a | b     | c    | d    |
+ ---+-------+------+------+
| 1 | 10000 |    4 |    5 |
+ ---+-------+------+------+
binlog格式:

1__mysql_dbconsole031221____expect_.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@test 02:37:04> replace  into  lingluo  values (4,10000,6,5);
Query OK, 1 row affected (0.00 sec)
root@test 02:37:59> replace  into  lingluo  values (6,10000,6,5); -------已經存在記錄,且UK同時衝突的時候,直接update
Query OK, 2  rows  affected (0.00 sec) ---------------------------------affect_rows是2
 
root@test 02:40:31> select  from  lingluo;
+ ---+-------+------+------+
| a | b     | c    | d    |
+ ---+-------+------+------+
| 1 | 10000 |    4 |    5 |
| 3 | 10000 |    5 |    5 |
| 6 | 10000 |    6 |    5 |
+ ---+-------+------+------+
rows  in  set  (0.00 sec)
binlog格式:

1__mysql_dbconsole031221____expect_.png

疑問:算法

既然uk衝突的時候是update,那麼爲何affect_rows都是2呢?讓咱們從源碼上分析看下:sql

 

 

 

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 :
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)
 
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)
 
root@test 03:34:40> replace  into  u (age,d) values (0,130);
Query OK, 2  rows  affected, 1 warning (0.01 sec)
 
root@test 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)
 
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)

用的時候須要注意的是:安全

  1. 若是指定replace列的話,儘可能寫全,要否則沒有輸入值的列數據會被賦成默認值(由於是先delete在insert),就和普通的insert是同樣的,因此若是你要執行replace語句的話是須要insert和delete權限的。spa

    若是你須要執行 SET col_name = col_name + 1,就至關於執行col_name = DEFAULT(col_name) + 1.日誌

  2. replace語句若是不深刻看的話,就和insert同樣,執行完後沒什麼反應code

例:索引

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
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)
 
root@test 04:20:10> replace  into  u (id,d)  values (8,232);
Query OK, 1 row affected (0.01 sec)
 
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)
 
root@test 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的行,而後插入新行
 
root@test 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 |
+ ----+------+------+
rows  in  set  (0.00 sec)
 
root@test 04:20:55>

MySQL給replace和load data....replace用的算法是:ci

  1. 嘗試向表裏插入新行源碼

  2. 當表裏惟一索引或者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格式:

結論

 

  1. 當存在pk衝突的時候是先delete再insert

  2. 當存在uk衝突的時候是直接update

相關文章
相關標籤/搜索