MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入

news2025/1/17 0:08:36

文章目录

  • 一、ON DUPLICATE KEY UPDATE的介绍
  • 二、ON DUPLICATE KEY UPDATE的使用
    • 2.1、案例一:根据主键id进行更新
    • 2.2、案例二:根据唯一索引进行更新(常用)
    • 2.3、案例三:没有主键或唯一键字段值相同就插入
    • 2.4、案例四:主键与唯一键字段同时存在
  • 三、ON DUPLICATE KEY UPDATE的注意事项
    • 3.1、on dupdate key update之后values的使用事项
    • 3.2、对values使用判断
    • 3.3、唯一索引大小写敏感问题
  • 四、ON DUPLICATE KEY UPDATE与mybatis联合使用
    • 4.1、写法一:与values()联合使用
    • 4.2、写法二:使用#{}
  • 五、ON DUPLICATE KEY UPDATE的缺点及坑
    • 5.1、ON DUPLICATE KEY UPDATE每次更新导致id不连续
    • 5.2、death lock死锁

有时候由于业务需求,可能需要先去根据某一字段值查询数据库中是否有记录,有则更新,没有则插入。这个时候就可以用到ON DUPLICATE KEY UPDATE这个sql语句了。

以下内容基于本地windows环境mysql:8.0.34进行讲解。

一、ON DUPLICATE KEY UPDATE的介绍

基本用法:ON DUPLICATE KEY UPDATE​​是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突(即已存在相同的唯一键值),则会执行更新操作,而不是抛出异常或忽略该条数据。这个语法可以大大简化我们的代码,减少不必要的判断和查询操作。

用法总结
1:on duplicate key update 语句根据主键id或唯一键来判断当前插入是否已存在。
2:记录已存在时,只会更新on duplicate key update之后指定的字段。
3:如果同时传递了主键和唯一键,以主键为判断存在依据,唯一键字段内容可以被修改。
4:唯一键大小写敏感时,大小写不同的值被认为是两个值,执行插入。参见下文中的大小写敏感问题

二、ON DUPLICATE KEY UPDATE的使用

准备表结构及测试数据, 注意:name是唯一键

drop table if exists tbl_test;
create table tbl_test(
	id int primary key auto_increment,
	name varchar(30) unique not null,
	age int comment '年龄',
	address varchar(50) comment '住址',
	update_time datetime default null
) comment '测试表';

insert into tbl_test(name,age,address,update_time) values('zhangsan',20,'杭州',now()),('lisi',21,'武汉',now());

测试数据如下:
在这里插入图片描述

2.1、案例一:根据主键id进行更新

on dupdate key update 语句基本功能是:当表中没有原来记录时,就插入,有的话就更新。

如下sql:

insert into tbl_test(id,name,age,address,update_time) values(1,'zhangsan1',201,'杭州1','2024-03-05 15:59:35')
on duplicate key update
age = values(age), -- 注意:values()括号中的内容是字段名称。比如:在java程序中使用时表字段可能叫user_name, 实体类中是userName,values()里面要填user_name
address = values(address),
update_time=now();

在这里插入图片描述

从执行结果可以看出,更新了id为1的age,address两个字段,而name字段没有修改生效。由此我们可以得出两个重要结论:
1:on duplicate key update 语句根据主键id来判断当前插入是否已存在。
2:已存在时,只会更新on duplicate key update之后限定的字段。

2.2、案例二:根据唯一索引进行更新(常用)

根据唯一索引进行更新是生产中比较常用的方式,因为id一般使用的是自增,很少会先把id查询出来,然后根据id进行更新。

如下sql:

insert into tbl_test(name,age,address) values('zhangsan',202,'杭州2')
on duplicate key update
age = values(age), -- 注意:values()括号中的内容是字段名称。比如:在java程序中使用时表字段可能叫user_name, 实体类中是userName,values()里面要填user_name
address = values(address),
update_time=now();

在这里插入图片描述

从执行结果看,这次没有传id,但是age,address字段仍然更新了。
由此可以得出另一个结论:
3:on duplicate key update 语句也可以根据唯一键来判断当前插入的记录是否已存在。

2.3、案例三:没有主键或唯一键字段值相同就插入

如下sql:

insert into tbl_test(name,age,address) values('zhangsan3',203,'杭州3')
on duplicate key update
age = values(age), -- 注意:values()括号中的内容是字段名称。比如:在java程序中使用时表字段可能叫user_name, 实体类中是userName,values()里面要填user_name
address = values(address),
update_time=now();

在这里插入图片描述
这条执行就比较简单了,没有主键或唯一键字段值相同,即判断当前记录不存在,新插入一条。

注意: 这里我们发现主键id并没有连续,直接从2变成了4,具体原理可见《MySQL数据库设置主键自增、自增主键为什么不能保证连续递增》

2.4、案例四:主键与唯一键字段同时存在

如下sql:

insert into tbl_test(id,name,age,address) values(1,'zhangsan4',204,'杭州4')
on duplicate key update
name = values(name),
age = values(age), -- 注意:values()括号中的内容是字段名称。比如:在java程序中使用时表字段可能叫user_name, 实体类中是userName,values()里面要填user_name
address = values(address),
update_time=now();

在这里插入图片描述
从上面可以看出,连唯一键name也被修改了。结论:
4:如果传递了主键,是可以修改唯一键字段内容的。
这里要注意,如果这里的name修改为 lisi,zhangsan3
会报唯一键冲突的。可以自行尝试。

三、ON DUPLICATE KEY UPDATE的注意事项

3.1、on dupdate key update之后values的使用事项

如下sql:

insert into tbl_test(name,age,address,update_time) values('zhangsan4',205,'杭州5','2024-03-05 00:00:00')
on duplicate key update
age = age,
address = '杭州',
update_time=values(update_time);

在这里插入图片描述
on dupdate key update之后没有用values的情况
分为两种情况:
1:如果为如上面的address= “杭州”,则会一直更新为"杭州".
2:如果为如上面的age = age,则age会保持数据库中的值,不会更新。
3:只有当使用了values后,才会更新为上下文中传入的值

3.2、对values使用判断

如下sql

insert into tbl_test(id,name,age,address) values(1,'zhangsan',202,'杭州2')
on duplicate key update
name = ifnull(values(name),name),
age = values(age)

达到的效果是,如果传入的name值为null,则不更新。不为null则更新。这里与mybatis配合使用比较好。

3.3、唯一索引大小写敏感问题

思考这么一个问题:如上面name作为唯一索引,当name大小写敏感时且数据库中存储了name=“zhangsan” ,那么再插入name="ZHANGSAN"是更新还是新增?

1):唯一索引大小写不敏感时
设置name字段为唯一索引且大小写不敏感

drop table if exists tbl_test;
create table tbl_test(
	id int primary key auto_increment,
	name varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci unique not null,
	age int comment '年龄',
	address varchar(50) comment '住址',
	update_time datetime default null
) comment '测试表';

insert into tbl_test(name,age,address,update_time) values('zhangsan',20,'杭州',now());
insert into tbl_test(name,age,address,update_time) values('ZHANGSAN',21,'杭州1',now());

在这里插入图片描述
可以看到当字段为大小写不敏感时zhangsan跟ZHANGSAN被认为是同一个值,不能重复插入。

当数据库中name=zhangsan时且name字段大小写不敏感时,我们看一下name="ZHANGSAN"能否更新成功?

insert into tbl_test(name,age,address,update_time) values('ZHANGSAN',22,'杭州2','2024-03-05 00:00:00')
on duplicate key update
age = values(age),
address = values(address),
update_time=values(update_time);

在这里插入图片描述
以上结果可以看出,当大小写不敏感时on duplicate key update是可以更新成功的,即认为是同一个值。

2):唯一索引大小写敏感时
设置name字段为唯一索引且大小写敏感

drop table if exists tbl_test;
create table tbl_test(
	id int primary key auto_increment,
	name varchar(30) CHARACTER SET utf8 COLLATE utf8_bin unique not null,
	age int comment '年龄',
	address varchar(50) comment '住址',
	update_time datetime default null
) comment '测试表';

insert into tbl_test(name,age,address,update_time) values('zhangsan',20,'杭州',now());
insert into tbl_test(name,age,address,update_time) values('ZHANGSAN',21,'杭州1',now());

在这里插入图片描述
可以看到当字段为大小写敏感时zhangsan跟ZHANGSAN被认为是两个值,插入了两条记录。所以此时用on duplicate key update会执行新增操作

四、ON DUPLICATE KEY UPDATE与mybatis联合使用

4.1、写法一:与values()联合使用

注意:values后面的内容是表字段名称即带下划线,而不是实体类驼峰名称

如下sql: dept_id为主键或唯一索引

<insert id="replaceInto">
    INSERT INTO sys_dept(
        dept_id,
        parent_id,
        status,
        update_time) 
    VALUES
    <foreach collection="deptList" item="item" separator=",">
        (#{item.deptId},
        #{item.parentId},
        #{item.status},
        #{item.updateTime})
    </foreach>
    ON DUPLICATE KEY UPDATE
        parent_id=VALUES(parent_id),
        status=VALUES(status),
        update_time=VALUES(update_time)
</insert>

4.2、写法二:使用#{}

如下sql: dept_id为主键或唯一索引

<insert id="replaceInto">
    INSERT INTO sys_dept(
        dept_id,
        parent_id,
        status,
        update_time) 
    VALUES
    <foreach collection="deptList" item="item" separator=",">
        (#{item.deptId},
        #{item.parentId},
        #{item.status},
        #{item.updateTime})
    </foreach>
    ON DUPLICATE KEY UPDATE
    <foreach collection="deptList" item="item" separator=",">
	       parent_id =  #{item.parentId},
	       status = #{item.status},
	       update_time = #{item.updateTime}
    </foreach>
</insert>

五、ON DUPLICATE KEY UPDATE的缺点及坑

5.1、ON DUPLICATE KEY UPDATE每次更新导致id不连续

如下sql:

drop table if exists tbl_test;
create table tbl_test(
	id int primary key auto_increment,
	name varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci unique not null,
	age int comment '年龄',
	address varchar(50) comment '住址',
	update_time datetime default null
) comment '测试表';

insert into tbl_test(name,age,address,update_time) values('zhangsan',20,'杭州',now()),('李四',21,'武汉',now());

在这里插入图片描述
执行on duplicate key update进行更新,然后再插入一条新的数据

insert into tbl_test(name,age,address,update_time) values('zhangsan',22,'杭州2','2024-03-05 00:00:00')
on duplicate key update
age = values(age),
address = values(address),
update_time=values(update_time);

insert into tbl_test(name,age,address,update_time) values('王五',23,'深圳',now());

在这里插入图片描述
可以看到id自增值从2直接变成了4,造成了id的不连续。

1.ON DUPLICATE KEY UPDATE每次更新导致id不连续原理:
mysql中有个配置值是innodb_autoinc_lock_mode。
innodb_autoinc_lock_mode中有3中模式,0,1和2,mysql5的默认配置是1,

  • 0是每次分配自增id的时候都会锁表.
  • 1只有在bulk insert的时候才会锁表,简单insert的时候只会使用一个light-weight mutex,比0的并发性能高
  • 2.没有仔细看,好像是很多的不保证…不太安全.

数据库默认是1的情况下,就会发生上面的那种现象,每次使用insert into … on duplicate key update 的时候都会把简单自增id增加,不管是发生了insert还是update

5.2、death lock死锁

经常看到网上说ON DUPLICATE KEY UPDATE会导致死锁,确实是存在这个可能的,不过由于目前没有特别好的方案,所以也只能使用这个sql语法了。在执行insert ... on duplicate key语句时,如果不对同一个表同时进行并发的insert或者update,基本不会造成死锁。即insert ... on duplicate key时尽量单线程串行进行新增或更新

insert … on duplicate key 在执行时,innodb引擎会先判断插入的行是否产生重复key错误,如果存在,在对该现有的行加上S(共享锁)锁,如果返回该行数据给mysql,然后mysql执行完duplicate后的update操作,然后对该记录加上X(排他锁),最后进行update写入。

如果有两个事务并发的执行同样的语句,那么就会产生death lock,如:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1513437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#、C++、Java、Python 选择哪个好?

作者&#xff1a;网博汇智 链接&#xff1a;https://www.zhihu.com/question/298323023/answer/2789627224 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 一个好的程序员不能把自己绑定在一种语言上&#xff0c;不…

element ui 中文离线文档(百度云盘下载)

一般内网开发上不了网&#xff0c;用离线版本比较方便&#xff0c;下载地址&#xff1a; https://download.csdn.net/download/li836779537/88355878?spm1001.2014.3001.5503 下载后里面有个 index.hrml 双击打开就可以用 效果如下&#xff1a;

Android基础开发-选择图片,发送彩信

发送图片彩信案例&#xff1a; 按住加号&#xff0c;选择相册&#xff0c;把相册选择的图片加载的应用中&#xff0c;点击发送彩信&#xff0c;选择短信&#xff0c;发送彩信。 代码如下&#xff1a; package com.example.client;import androidx.activity.result.ActivityRe…

redis-操作数据库

0 序言 一个Redis服务器可以包含多个数据库。在默认情况下&#xff0c;Redis服务器在启动时将会创建16个数据库&#xff1a;这些数据库都使用号码进行标识&#xff0c;其中第一个数据库为0号数据库&#xff0c;第二个数据库为1号数据库&#xff0c;而第三个数据库则为2号数据库…

Prompt进阶3:LangGPT(构建高性能质量Prompt策略和技巧2)--稳定高质量文案生成器

Prompt进阶3:LangGPT(构建高性能质量Prompt策略和技巧2)–稳定高质量文案生成器 1.LangGPT介绍 现有 Prompt 创建方法有如下缺点&#xff1a; 缺乏系统性&#xff1a;大多是细碎的规则&#xff0c;技巧&#xff0c;严重依赖个人经验缺乏灵活性&#xff1a;对他人分享的优质 …

高铁列车员信息宣传向媒体投稿有哪些方法?

作为一名高铁列车工作人员,我肩负着传递高铁精神、展示列车员风采的重要使命。每月,我都要完成单位对外信息宣传的考核任务,通过媒体投稿来发表列车员的信息宣传文章。在这条信息宣传之路上,我经历了从摸着石头过河到智慧投稿的蜕变,其中的心酸与轻松交织,成为了我职业生涯中难…

Android 11 最终 Beta 版发布,正式版即将到来!

Beta 3 中的更新 本次更新包括针对 Pixel 设备和 Android 模拟器的 Android 11 发布候选版本。我们在 Beta 2 时就已经达到了平台稳定性里程碑&#xff0c;即所有面向应用的接口和行为都已敲定&#xff0c;包括 SDK 和 NDK API、面向应用的系统行为&#xff0c;以及对非 SDK 接…

【Linux】常用指令大全 [万字详解!建议收藏记忆!]

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

逆变器专题(21)-并网电压前馈(消除弱电网影响)(1)

在逆变器并网系统中&#xff0c;并网电压前馈是整个系统中较为重要的一环&#xff0c;一方面&#xff0c;他可以在一定程度上增加系统的快速响应能力&#xff0c;另一方面&#xff0c;其可以 消除电网电压中的低次谐波对并网输出电流的影响&#xff0c;使得整个系统的并网电能质…

spring boot 学习

目录 引言&#xff1a; 一、Spring Boot概述 二、Spring Boot的核心特性 1 自动配置 2 起步依赖 3 内嵌容器 4 监控与管理 三、Spring Boot的入门步骤 1 环境安装 2 创建项建 3 编写代码 1 启动类 2 控制器 3服务 4自动装配 5配置属性 6 JPA实体 4 运行与调试…

【Linux】linux的常用命令

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;MQ ⛺️稳中求进&#xff0c;晒太阳 (Linux常用命令) finalShell 1. ls命令 作用&#xff1a;显示当前目录下的文件及文件夹 举例&#xff1a;在用户目录&#xff08;root)使用ls可以查…

【Python】新手入门学习:详细介绍开放封闭原则(OCP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍开放封闭原则&#xff08;OCP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyT…

网络通信另个角度的认识(进程间通信),端口号(为什么要有,和pid的关系,分类,如何封装,和进程的定位原理+对应关系),客户端如何拿到服务端的port

目录 另一个角度认识网络通信 端口号 引入 -- 为什么要有端口号 问题 解决 端口号和pid 举例 介绍 分类 知名端口 注册端口 动态端口 客户端如何知道服务端的端口号 封装端口号 定位原理 进程和端口号的对应关系 数据如何被上层进程读到 另一个角度认识网络…

指数函数与三角函数乘积的积分公式

先上公式&#xff0c;公式正确&#xff0c;用欧拉公式即可证明。 数学公式不好打字&#xff0c;我就发截图。 欧拉公式证明如下&#xff1a;

汉诺塔问题代码写法的详细解析

汉诺塔游戏规则&#xff1a; 规则&#xff1a; 汉诺塔问题是一个经典的问题。汉诺塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又称河内塔&#xff0c;源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着…

基于Web的论文管理系统设计

目 录 目 录 III 摘 要 V 关键词 V Abstract VI Key Word VI 第一章 绪论 6 1.1系统设计背景 1 1.2系统设计目的与意义 1 1.3国内外现状 2 1.4本文结构 3 第二章 需求分析 3 2.1系统需求分析 4 2.2系统角色设计 4 第三章 系统开发技术 4 3.1 PHP语言简介和特点 5 3.2 Mysql数据…

代码随想录day18(1)二叉树:二叉树的层序遍历(leetcode102)

题目要求&#xff1a;实现二叉树的层序遍历 思路&#xff1a;实现层序遍历使用队列即可。每个结点遍历结束后&#xff0c;将其左右孩子结点也放入队列。本道题返回的是一个二维数组&#xff0c;即需要将每一层分开&#xff0c;所以还需要size变量记录每层元素个数。注意&#…

程序员来看,你选哪一门编程语言提升自己?

通过多个调查表的分析&#xff0c;发现大家对于GO语言的兴趣和需求非常高。GO语言是一种由Google开发的静态类型、编译型语言&#xff0c;最初于2007年问世。这门语言的设计者是Robert Griesemer、Rob Pike和Ken Thompson&#xff0c;他们的初衷是为了弥补C和Java在大规模软件工…

I2C学习总结

i2c概述 I2C&#xff08;Inter-Intergreted Circuit&#xff09; 是一种串行通信协议&#xff0c;用于集成电路之间完成数据传输&#xff0c;i2c用广泛用以各种领域&#xff0c;包括电子设备、嵌入式系统、工业自动化等&#xff1b; i2c仅仅只是一个数据传输的协议&#xff0c…

2024年3月9日美团笔试解答

这题打卡题&#xff0c;先扫描一遍原本有n个M和T&#xff0c;然后总数减一下&#xff0c;剩下m个&#xff0c;再看可以添加k个&#xff0c;返回nmin(m,k) Python解答 import time import bisect import functools import math import os import random import re import sys …