数据重复更新操作Insert ignore 、insert into、insert...on duplicate key update、replace into

Insert ignore 与insert into的区别

INSERT INTO插入数据,如果数据存在,则报错

INSERT IGNORE会忽略数据库中已经存在的数据,如果数据库没有数据,就插入新的数据,如果有数据的话就跳过这条数据。这样就可以保留数据库中已经存在数据,达到在间隙中插入数据的目的。

replace into

replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引的话,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;

REPLACE语句会返回一个数,来指示受影响的行的数目。该数是被删除和被插入的行数的和。

如果对于一个单行REPLACE该数为1,则一行被插入,同时没有行被删除。

如果该数大于1,则在新行被插入前,有一个或多个旧行被删除。

如果表包含多个唯一索引,并且新行复制了在不同的唯一索引中的不同旧行的值,则有可能是一个单一行替换了多个旧行。

新建表

CREATE TABLE `atest` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`age` int(32) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `url` (`url`) USING BTREE,
UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;

插入数据

INSERT INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (1, 'aa', 'aa1', 11);
INSERT INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (2, 'bb', 'bb2', 22);
INSERT INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (3, 'cc', 'cc3', 33);
INSERT INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (4, 'dd', 'dd4', 44);

此时进行replace操作

replace INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (2, 'dd', 'cc3', 4433);

操作之后的结果

replace INTO `apiboot`.`atest`(`id`, `url`, `name`, `age`) VALUES (2, 'dd', 'cc3', 4433) Affected rows: 4, Time: 0.037000s

数据库的数据:
id url name age
1	 aa	 aa1	11
2	 dd	 cc3	4433

分析:因为url和name都是唯一索引,上面replace操作的sql的数据涉及到3条数据,id是2涉及到第2条数据,url是dd涉及到第4条数据,name是cc3涉及到第三条数据,此时进行replace操作会将涉及到的第2条、第3条和第4条数据都删掉,同时插入id为2的这条数据,所以受到影响的数据是4行

插入数据的表必须有主键或者是唯一索引!否则的话,replace into 会直接插入数据,这将导致表中出现重复的数据。

MySQL replace into 有三种形式:

  1. replace into tbl_name(col_name, ...) values(...)

  2. replace into tbl_name(col_name, ...) select ...

  3. replace into tbl_name set col_name=value, ...

第一种形式类似于insert into的用法,

第二种replace select的用法也类似于insert select,这种用法并不一定要求列名匹配,事实上,MYSQL甚至不关心select返回的列名,它需要的是列的位置。例如,replace into tb1( name, title, mood) select rname, rtitle, rmood from tb2;这个例子使用replace into从tb2中将所有数据导入tb1中。

第三种replace set用法类似于update set用法,使用一个例如“SET col_name = col_name + 1”的赋值,则对位于右侧的列名称的引用会被作为DEFAULT(col_name)处理。因此,该赋值相当于SET col_name = DEFAULT(col_name) + 1。

前两种形式用的多些。其中 “into” 关键字可以省略,不过最好加上 “into”,这样意思更加直观。另外,对于那些没有给予值的列,MySQL 将自动为这些列赋上默认值。

insert...on duplicate key update语法详解

有一些场景,如日志文件解析入库,消息队列接收数据入库等情况下可能解析到或者接收到待插入的重复数据****存在重复数据则更新不存在则插入

这时如下语句的写法就派上用场了,on duplicate key update的作用也就是说存在重复数据则更新,不存在则插入。

说明:

  1. on duplicate key update 含义:
    1)如果在INSERT语句末尾指定了 on duplicate key update,如果插入会导致UNIQUE索引或PRIMARY KEY出现重复值,则在出现重复值的行执行UPDATE;
    2)如果不会导致唯一值列重复的问题,则插入新行。

  2. values(col_name)函数只是取当前插入语句中的插入值,并没有累加功能。
    如:count = values(count) 取前面 insert into 中的 count 值,并更新当有多条记录冲突,需要插入时,前面的更新值都被最后一条记录覆盖,所以呈现出取最后一条更新的现象。
    如:count = count + values(count) 依然取前面 insert into 中的 count 值,并与原记录值相加后更新回数据库,这样,当多条记录冲突需要插入时, 就实现了不断累加更新的现象。

注:insert into ... on duplicate key update ... values() 这个语句
尽管在冲突时执行了更新,并没有插入,但是发现依然会占用 id 序号(自增),出现很多丢失的 id 值

函数使用说明:在一个 INSERT … ON DUPLICATE KEY UPDATE … 语句中,你可以在 UPDATE 子句中使用 VALUES(col_name ) 函数,用来访问来自该语句的 INSERT 部分的列值。换言之,UPDATE 子句中的 VALUES(col_name ) 访问需要被插入的 col_name 的值 , 并不会发生重复键冲突。这个函数在多行插入中特别有用。 VALUES() 函数只在 INSERT ... UPDATE 语句中有意义,而在其它情况下只会返回 NULL。

**案例:
0. 创建案例表 word_count_0626(单词计数表)
  use test;
  CREATE TABLE IF NOT EXISTS word_count_0626 (
  	id int(11) NOT NULL AUTO_INCREMENT,
  	word varchar(64) NOT NULL,
  	count int(11) DEFAULT 0,
  	date date NOT NULL,
  	PRIMARY KEY (id),
  	UNIQUE KEY word (word, date)  // (word,date) 两字段组合唯一
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  
注:curdate() 为 "2019-06-26"
1. 执行第一次:(首次数据库表中没有数据,正常插入)
  insert into word_count_0626 (word, count, date) values 
  ('a',5,curdate()) 
  on duplicate key update count=values(count);
  # 结果显示:
  id   word    count    date 
  1    a       5        2019-06-26
  
2. 执行第二次:(与第一次的唯一(word,date)冲突,执行更新)
  insert into word_count_0626 (word, count, date) values 
  ('a',6,curdate()) 
  on duplicate key update count=values(count);
  # 结果显示:
  id   word    count    date 
  1    a       6        2019-06-26  (更新)
  
3. 执行第三次:
  insert into word_count_0626 (word, count, date) values 
  ('a',6,curdate()-1),    // 取前一天,不会冲突
  ('a',7,curdate()) // 冲突
  on duplicate key update count=values(count);
  # 结果显示:
  id   word    count    date 
  1    a       7        2019-06-26  (更新)
  3    a       6        2019-06-25  (新插入)
  
4. 执行第四次:(更新冲突的最后一条插入值)
  insert into word_count_0626 (word, count, date) values 
  ('a',2,curdate()),  // 冲突
  ('a',1,curdate())  // 冲突
  on duplicate key update count=values(count);
  # 结果显示:
  id   word    count    date 
  1    a       1        2019-06-26  (更新最后一条插入值)
  3    a       6        2019-06-25  (不变)
  
5. 执行第五次:(更新冲突的累加插入值)
  insert into word_count_0626 (word, count, date) values 
  ('a',2,curdate()),
  ('a',1,curdate()) 
  on duplicate key update count=count+values(count); // 实现每行累加
  # 结果显示:
  id   word    count    date 
  1    a       4        2019-06-26
  3    a       6        2019-06-25
 
6. 执行第六次:(无冲突插入,观察 id 键值,出现了很多丢失,id 直接跳到了 9)
  insert into word_count_0626 (word, count, date) values 
  ('b',2,curdate())
  on duplicate key update count=count+values(count);
  # 结果显示:
  id   word    count    date 
  1    a       4        2019-06-26
  3    a       6        2019-06-25
  9    b       2        2019-06-26

replace与nsert...on duplicate key update不同之处

replace是删除记录,然后再重新insert,而insert...on duplicate key update是直接在该条记录上修改,所以二者的差别主要有以下两处:

1、当表中存在自增值的时候,如果表中存在某条记录,replace语法会导致自增值+1,而insert...on duplicate key update语法不会;

2、当表中的某些字段中包含默认值的时候,replace操作插入不完全字段的记录,会导致其他字段直接使用默认值,而insert...on duplicate key update操作会保留该条记录的原有值。

站在巨人肩膀上摘苹果

https://www.cnblogs.com/stevin-john/p/4768904.html

https://www.cnblogs.com/c-961900940/p/6197878.html

https://blog.csdn.net/ZYC88888/article/details/104250372

原文地址:https://www.cnblogs.com/eternityz/p/15330050.html