前段时间写游戏合服工具时出现过一个问题,源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 test\G
*************************** 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了。
 

MySQL REPLACE INTO 的使用的更多相关文章

  1. MySQL replace into 使用详解 及 注意事项

    REPLACE的运行与INSERT很相似.只有一点例外,假如表中的一个旧记录与一个用于PRIMARY KEY或一个UNIQUE索引的新记录具有相同的值,则在新记录被插入之前,旧记录被删除.注意:除非表 ...

  2. MySQL replace函数替换字符串语句的用法(mysql字符串替换)

    MySQL replace函数我们经常用到,下面就为您详细介绍MySQL replace函数的用法,希望对您学习MySQL replace函数方面能有所启迪. 最近在研究CMS,在数据转换的时候需要用 ...

  3. MySQL "replace into" 的坑

    MySQL 对 SQL 有很多扩展,有些用起来很方便,但有一些被误用之后会有性能问题,还会有一些意料之外的副作用,比如 REPLACE INTO. 比如有这样一张表: CREATE TABLE `au ...

  4. MySQL replace into 说明(insert into 增强版)

    MySQL replace into 说明(insert into 增强版) 在插入数据到一个表时,通常是这种情况:1. 先推断数据是否存在: 2. 假设不存在,则插入:3.假设存在,则更新. 在 S ...

  5. MySQL replace into (insert into 的增强版)

    在使用SQL语句进行数据表插入insert操作时,如果表中定义了主键,插入具有相同主键的记录会报错:  Error Code: 1062. Duplicate entry 'XXXXX' for ke ...

  6. mysql replace into 的使用情况

    replace into的存在的几种情况 当表存在主键并且存在唯一键的时候 如果只是主键冲突 mysql> select * from auto; +----+---+------+------ ...

  7. 细说mysql replace into

    replace语句在一般的情况下和insert差不多,但是如果表中存在primary 或者unique索引的时候,如果插入的数据和原来的primary key或者unique相同的时候,会删除原来的数 ...

  8. Mysql replace into

    mysqlsql serverinsert 在向表中插入数据的时候,经常遇到这样的情况:1. 首先判断数据是否存在: 2. 如果不存在,则插入:3.如果存在,则更新. 在 SQL Server 中可以 ...

  9. MySQL "replace into" 的坑以及insert相关操作

    下面我们主要说一下在插入时候的几种情况: 1:insert ignore 2:replace into 3:ON DUPLICATE KEY UPDATE 关于insert ignore: 关于rep ...

  10. MySQL replace into 用法

    讨人喜欢的 MySQL replace into 用法(insert into 的增强版) 在向表中插入数据的时候,经常遇到这样的情况:1. 首先判断数据是否存在: 2. 如果不存在,则插入:3.如果 ...

随机推荐

  1. 《自拍教程46》Python_adb自动拍照100张

    Android手机测试, 涉及照相机(Camera)应用程序的稳定性测试的用例, 需要涉及100张照片的拍照自动化测试. 准备阶段 先清理老照片,照片一般存放在/scard/DCIM目录下 adb s ...

  2. maven如何安装oracle驱动jdbc的jar包

    一.因为oracle驱动的压缩包在maven官网上并没有提供,所以需要自己去手动下载. 二.下载安装步骤如下: (1)第一步:找到你本地的oracle驱动包所在位置: (2)在cmd中打开jar包所在 ...

  3. C# Linq方式生成SAP对接的XML格式内容(一般处理程序 ashx )

    Linq生成XML的方法: string CreateXML(string strkey, string strDATAJSON) { XDeclaration dec = new XDeclarat ...

  4. Python Django撸个WebSSH操作Kubernetes Pod(下)- 终端窗口自适应Resize

    追求完美不服输的我,一直在与各种问题斗争的路上痛并快乐着 上一篇文章Django实现WebSSH操作Kubernetes Pod最后留了个问题没有解决,那就是terminal内容窗口的大小没有办法调整 ...

  5. Android Studio Run/Debug configuration error: Module not specified

    如下图,配置时没有module可选,因此报错error: Module not specified 解决方法: 1.打开根目录的settings.gradle,删除include ':app' 2.在 ...

  6. ajax结合sweetalert弹出框删除数据

    思路:

  7. Mybatis详解系列(一)--持久层框架解决了什么及如何使用Mybatis

    简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository ...

  8. 【开源】使用Angular9和TypeScript开发RPG游戏(补充了Buffer技能)

    RPG系统构造 通过对于斗罗大陆小说的游戏化过程,熟悉Angular的结构以及使用TypeScript的面向对象开发方法. Github项目源代码地址 RPG系统构造 ver0.02 2020/03/ ...

  9. 03.第一个Go程序

    第一个Go程序 Hello World 现在我们来创建第一个Go项目--hello.在我们的GOPATH下的src目录中创建hello目录. 在该目录中创建一个main.go文件: package m ...

  10. Building Applications with Force.com and VisualForce (DEV401) (二二):Visualforce Componets (Tags) Library Part II

    Dev401-023:Visualforce Pages: Visualforce Componets (Tags) Library Part II   Apex:pageBlockTable1.A ...