zoukankan      html  css  js  c++  java
  • MySQL REPLACE INTO 的使用

    前段时间写游戏合服工具时出现过一个问题,源DB和目标DB角色表中主键全部都不相同,从源DB取出玩家数据再使用 replace into 写入目标DB中,结果总有几条数据插入时会导致目标DB中原有的角色数据丢失了。仔细排查之后发现时replace into使用错误造成的。在这里总结下replace into的使用方式,可以帮助有幸看到这篇文章的朋友避开replace into 使用的坑。

     

    replace into 执行流程

    1. 尝试向表中插入新行

    2. 插入失败,因为表中存在相同的主键或唯一索引
       
        a.删除表中所有相同的主线以及唯一索引的记录
       
        b.再次尝试向表中插入新行
     

    与insert的区别

    insert是直接插入记录,如果表中存在相同的主键或唯一索引,插入失败。
     
    replace into也是插入记录,如果表中存在相同的主键或唯一索引,先删除相同主键或唯一索引记录,再执行插入操作。如果表中不存在相同主键或唯一索引时,和insert时相同的。
     

    注意

    1. 使用replace into时需要对表有delete和insert的权限

    2. replace into语句中所有缺失的字段都会被设置为字段的默认值

    3. replace into执行记结果受影响的行数大于1行时,插入操作只有一行受影响,其他受影响的行是删除操作

    4. replace..set col_name = col_name + 1时,col_name会被当做默认值,赋值最终等价于 col_name = DEFAULT(col_name) + 1
     
     
    假如有如下的表:
    mysql> show create table testG
    *************************** 1. row ***************************
           Table: test
    Create Table: CREATE TABLE `test` (
      `id` int(11) NOT NULL,
      `name` char(32) NOT NULL,
      `uid` int(11) NOT NULL,
      `age` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_name` (`name`),
      KEY `idx_uid` (`uid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    
    
    
    mysql> replace into test(id,name,uid,age) values(1,'aa',101,20),(2,'bb',102,21);
    Query OK, 2 rows affected (0.01 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    
    
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  1 | aa   | 101 |   20 |
    |  2 | bb   | 102 |   21 |
    +----+------+-----+------+
    2 rows in set (0.00 sec)

    现在执行下面的操作:

    mysql> replace into test(id,name,uid,age) values(2,'cc',103,22);      
    Query OK, 2 rows affected (0.00 sec)
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  1 | aa   | 101 |   20 |
    |  2 | cc   | 103 |   22 |
    +----+------+-----+------+
    2 rows in set (0.00 sec)

    replace into语句执行之后,2行受影响,主键id=2在表中已经存在,先删除表中主键,然后再插入新行数据

    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  1 | aa   | 101 |   20 |
    |  2 | cc   | 103 |   22 |
    +----+------+-----+------+
    2 rows in set (0.00 sec)
    mysql> replace into test(id,name,uid,age) values(2,'aa',100,25);
    Query OK, 3 rows affected (0.02 sec)
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |   25 |
    +----+------+-----+------+
    1 row in set (0.00 sec)
     
    replace into语句执行之后,3行受影响,主键id=2在表中已经存在,先删除表中主键行,唯一索引name='aa'再表中已经存在,先删除表中索引行,然后再插入新行数据
     
    mysql> replace into test(id,name,uid,age) values(3,'dd',100,25);   
    Query OK, 1 row affected (0.01 sec)
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |   25 |
    |  3 | dd   | 100 |   25 |
    +----+------+-----+------+
    2 rows in set (0.01 sec)
     
    replace into语句执行之后,1行受影响,表中没有相同的主键以及唯一索引相同,uid字段是普通索引,不是唯一索引,所以不会有删除操作,最终和insert语句效果一样,插入一行新数据
     
    mysql> replace into test set id = 3,name='dd',uid=100,age=age+1;
    Query OK, 2 rows affected (0.01 sec)
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |   25 |
    |  3 | dd   | 100 | NULL |
    +----+------+-----+------+
    2 rows in set (0.00 sec)
    mysql> select NULL + 1;
    +----------+
    | NULL + 1 |
    +----------+
    |     NULL |
    +----------+
    1 row in set (0.00 sec)
    mysql> update test set age = age + 1 where id = 2;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |   26 |
    |  3 | dd   | 100 | NULL |
    +----+------+-----+------+
    2 rows in set (0.00 sec)
     
     
    如果使用 replace...set col_name = col_name + 1 语句的话,col_name取的是默认值,这里age的默认值是NULL,set age = age + 1 等价于 set age = NULL + 1,结果还是为NULL
     
    使用update...set col_name = col_name + 1就不会有这个问题,这里的主要原因是 replace 会先删除重复主键或唯一索引的记录,再插入一行新数据,当删除原有数据之后 age 字段就没有值了。所以这里的 replace ...set age = age + 1,age 的最终值是NULL,我们修改下age的默认值,再执行replace...set age = age + 1看下结果
     
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |   26 |
    |  3 | dd   | 100 | NULL |
    +----+------+-----+------+
    2 rows in set (0.00 sec)
    mysql> alter table test alter age set default 3;   
    Query OK, 0 rows affected (0.01 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    mysql> replace into test set id = 2,name='aa',uid=100,age = age + 1;
    Query OK, 2 rows affected (0.07 sec)
    mysql> select * from test;
    +----+------+-----+------+
    | id | name | uid | age  |
    +----+------+-----+------+
    |  2 | aa   | 100 |    4 |
    |  3 | dd   | 100 | NULL |
    +----+------+-----+------+
    2 rows in set (0.00 sec)
    把age的默认值修改成3之后,执行replace..set age = age + 1, age最终的值不在是NULL了。
     
  • 相关阅读:
    在Flex or AIR中检测你的网络连接是否正常
    设置Adobe Air应用程序属性
    airMeeting
    用Adobe Flex3开发AIR应用程序–入门指南
    Adobe Flash Player 9.0.124 的安全策略更改
    分发,安装和运行AIR应用程序
    永远置于顶层(always on top)的AIR应用
    翻译:SWFObject 2.0官方文档
    C#温故而知新学习系列之.NET框架高级特性—概述.NET框架中的反射(一)
    C#温故而知新学习系列之字符串处理—指定字符串的显示格式(一)
  • 原文地址:https://www.cnblogs.com/wanng/p/mysql-replace-into.html
Copyright © 2011-2022 走看看