MySQL实战中,Insert语句的使用心得总结
wptr33 2024-11-08 15:02 54 浏览
一、Insert的几种语法
1-1.普通插入语句
INSERT INTO table (`a`, `b`, `c`, ……) VALUES ('a', 'b', 'c', ……);
这里不再赘述,注意顺序即可,不建议小伙伴们去掉前面括号的内容,别问为什么,容易被同事骂。
1-2.插入或更新
如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就更新该记录,此时,可以使用"INSERT INTO … ON DUPLICATE KEY UPDATE …"语句:
情景示例:这张表存了用户历史充值金额,如果第一次充值就新增一条数据,如果该用户充值过就累加历史充值金额,需要保证单个用户数据不重复录入。
这时可以使用"INSERT INTO … ON DUPLICATE KEY UPDATE …"语句。
注意事项:"INSERT INTO … ON DUPLICATE KEY UPDATE …"语句是基于唯一索引或主键来判断唯一(是否存在)的。如下SQL所示,需要在username字段上建立唯一索引(Unique),transId设置自增即可。
-- 用户陈哈哈充值了30元买会员
INSERT INTO total_transaction (t_transId,username,total_amount,last_transTime,last_remark)
VALUES (null, 'chenhaha', 30, '2020-06-11 20:00:20', '充会员')
ON DUPLICATE KEY UPDATE total_amount=total_amount + 30, last_transTime='2020-06-11 20:00:20', last_remark ='充会员';
-- 用户陈哈哈充值了100元买瞎子至高之拳皮肤
INSERT INTO total_transaction (t_transId,username,total_amount,last_transTime,last_remark)
VALUES (null, 'chenhaha', 100, '2020-06-11 20:00:20', '购买盲僧至高之拳皮肤')
ON DUPLICATE KEY UPDATE total_amount=total_amount + 100, last_transTime='2020-06-11 21:00:00', last_remark ='购买盲僧至高之拳皮肤';
若username='chenhaha'的记录不存在,INSERT语句将插入新记录,否则,当前username='chenhaha'的记录将被更新,更新的字段由UPDATE指定。
对了,ON DUPLICATE KEY UPDATE为MySQL特有语法,比如在MySQL迁移Oracle或其他DB时,类似的语句要改为MERGE INTO语法,兼容性让人想骂街。但没办法,就像用WPS写的xlsx用Office无法打开一样。
1-3.插入或替换
如果我们想插入一条新记录(INSERT),但如果记录已经存在,就先删除原记录,再插入新记录。
情景示例:这张表存的每个客户最近一次交易订单信息,要求保证单个用户数据不重复录入,且执行效率最高,与数据库交互最少,支撑数据库的高可用。
此时,可以使用"REPLACE INTO"语句,这样就不必先查询,再决定是否先删除再插入。
"REPLACE INTO"语句是基于唯一索引或主键来判断唯一(是否存在)的。
"REPLACE INTO"语句是基于唯一索引或主键来判断唯一(是否存在)的。
"REPLACE INTO"语句是基于唯一索引或主键来判断唯一(是否存在)的。
注意事项:如下SQL所示,需要在username字段上建立唯一索引(Unique),transId设置自增即可。
-- 20点充值
REPLACE INTO last_transaction (transId,username,amount,trans_time,remark)
VALUES (null, 'chenhaha', 30, '2020-06-11 20:00:20', '会员充值');
-- 21点买皮肤
REPLACE INTO last_transaction (transId,username,amount,trans_time,remark)
VALUES (null, 'chenhaha', 100, '2020-06-11 21:00:00', '购买盲僧至高之拳皮肤');
若username='chenhaha’的记录不存在,REPLACE语句将插入新记录(首次充值),否则,当前username='chenhaha’的记录将被删除,然后再插入新记录。
id不要给具体值,不然会影响SQL执行,业务有特殊需求除外。
小tips:
ON DUPLICATE KEY UPDATE:如果插入行出现唯一索引或者主键重复时,则执行旧的update;如果不会导致唯一索引或者主键重复时,就直接添加新行。
REPLACE INTO:如果插入行出现唯一索引或者主键重复时,则delete老记录,而录入新的记录;如果不会导致唯一索引或者主键重复时,就直接添加新行。
replace into 与 insert on deplicate udpate 比较:
- 1、在没有主键或者唯一索引重复时,replace into 与 insert on deplicate udpate 相同。
- 2、在主键或者唯一索引重复时,replace是delete老记录,而录入新的记录,所以原有的所有记录会被清除,这个时候,如果replace语句的字段不全的话,有些原有的比如c字段的值会被自动填充为默认值(如Null)。
- 3、细心地朋友们会发现,insert on deplicate udpate只是影响一行,而REPLACE INTO可能影响多行,为什么呢?写在文章最后一节咯~
1-4.插入或忽略
如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就啥事也不干直接忽略,此时,可以使用INSERT IGNORE INTO …语句:情景很多,不再举例赘述。
注意事项:同上,"INSERT IGNORE INTO …"语句是基于唯一索引或主键来判断唯一(是否存在)的,需要在username字段上建立唯一索引(Unique),transId设置自增即可。
-- 用户首次添加
INSERT IGNORE INTO users_info (id, username, sex, age ,balance, create_time)
VALUES (null, 'chenhaha', '男', 26, 0, '2020-06-11 20:00:20');
-- 二次添加,直接忽略
INSERT IGNORE INTO users_info (id, username, sex, age ,balance, create_time)
VALUES (null, 'chenhaha', '男', 26, 0, '2020-06-11 21:00:20');
二、大量数据插入
2-1、三种处理方式
2-1-1、单条循环插入
我们取10w条数据进行了一些测试,如果插入方式为程序遍历循环逐条插入。在mysql上检测插入一条的速度在0.01s到0.03s之间。
逐条插入的平均速度是0.02*100000,也就是33分钟左右。
下面代码是测试例子:
1普通循环插入100000条数据的时间测试
@Test
public void insertUsers1() {
User user = new User();
user.setUserName("提莫队长");
user.setPassword("正在送命");
user.setPrice(3150);
user.setHobby("种蘑菇");
for (int i = 0; i < 100000; i++) {
user.setUserName("提莫队长" + i);
// 调用插入方法
userMapper.insertUser(user);
}
}
执行速度是30分钟也就是0.018*100000的速度。可以说是很慢了
发现逐条插入优化成本太高。然后去查询优化方式。发现用批量插入的方法可以显著提高速度。
将100000条数据的插入速度提升到1-2分钟左右↓
2-1-2、修改SQL语句批量插入
insert into user_info (user_id,username,password,price,hobby)
values (null,'提莫队长1','123456',3150,'种蘑菇'),(null,'盖伦','123456',450,'踩蘑菇');
用批量插入插入100000条数据,测试代码如下:
@Test
public void insertUsers2() {
List<User> list= new ArrayList<User>();
User user = new User();
user.setPassword("正在送命");
user.setPrice(3150);
user.setHobby("种蘑菇");
for (int i = 0; i < 100000; i++) {
user.setUserName("提莫队长" + i);
// 将单个对象放入参数list中
list.add(user);
}
userMapper.insertListUser(list);
}
批量插入使用了0.046s 这相当于插入一两条数据的速度,所以用批量插入会大大提升数据插入速度,当有较大数据插入操作是用批量插入优化
批量插入的写法:
dao定义层方法:
Integer insertListUser(List<User> user);
mybatis Mapper中的sql写法:
<insert id="insertListUser" parameterType="java.util.List">
INSERT INTO `db`.`user_info`
( `id`,
`username`,
`password`,
`price`,
`hobby`)
values
<foreach collection="list" item="item" separator="," index="index">
(null,
#{item.userName},
#{item.password},
#{item.price},
#{item.hobby})
</foreach>
</insert>
这样就能进行批量插入操作:
注:但是当批量操作数据量很大的时候。例如我插入10w条数据的SQL语句要操作的数据包超过了1M,MySQL会报如下错:
报错信息:
Mysql You can change this value on the server by setting the max_allowed_packet' variable. Packet for query is too large (6832997 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
解释:
用于查询的数据包太大(6832997> 1048576)。 您可以通过设置max_allowed_packet的变量来更改服务器上的这个值。
通过解释可以看到用于操作的包太大。这里要插入的SQL内容数据大小为6M 所以报错。
解决方法:
数据库是MySQL57,查了一下资料是MySQL的一个系统参数问题:
max_allowed_packet,其默认值为1048576(1M),
查询:
show VARIABLES like '%max_allowed_packet%';
修改此变量的值:MySQL安装目录下的my.ini(windows)或/etc/mysql.cnf(linux) 文件中的[mysqld]段中的
max_allowed_packet = 1M,如更改为20M(或更大,如果没有这行内容,增加这一行),如下图
保存,重启MySQL服务。现在可以执行size大于1M小于20M的SQL语句了。
但是如果20M也不够呢?
2-1-3、分批量多次循环插入
如果不方便修改数据库配置或需要插入的内容太多时,也可以通过后端代码控制,比如插入10w条数据,分100批次每次插入1000条即可,也就是几秒钟而已;当然,如果每条的内容很多的话,另说。。
2-2、插入速度慢的其他几种优化途径
A、通过show processlist;命令,查询是否有其他长进程或大量短进程抢占线程池资源 ?看能否通过把部分进程分配到备库从而减轻主库压力;或者,先把没用的进程kill掉一些?(手动挠头o_O)
B、大批量导数据,也可以先关闭索引,数据导入完后再打开索引
关闭:ALTER TABLE user_info DISABLE KEYS;
开启:ALTER TABLE user_info ENABLE KEYS;
三、REPLACE INTO语法的“坑”
上面曾提到REPLACE可能影响3条以上的记录,这是因为在表中有超过一个的唯一索引。在这种情况下,REPLACE将考虑每一个唯一索引,并对每一个索引对应的重复记录都删除,然后插入这条新记录。假设有一个table1表,有3个字段a, b, c。它们都有一个唯一索引,会怎么样呢?我们早一些数据测试一下。
-- 测试表创建,a,b,c三个字段均有唯一索引
CREATE TABLE table1(a INT NOT NULL UNIQUE,b INT NOT NULL UNIQUE,c INT NOT NULL UNIQUE);
-- 插入三条测试数据
INSERT into table1 VALUES(1,1,1);
INSERT into table1 VALUES(2,2,2);
INSERT into table1 VALUES(3,3,3);
此时table1中已经有了3条记录,a,b,c三个字段都是唯一(UNIQUE)索引
mysql> select * from table1;
+---+---+---+
| a | b | c |
+---+---+---+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+---+---+---+
3 rows in set (0.00 sec)
下面我们使用REPLACE语句向table1中插入一条记录。
REPLACE INTO table1(a, b, c) VALUES(1,2,3);
mysql> REPLACE INTO table1(a, b, c) VALUES(1,2,3);
Query OK, 4 rows affected (0.04 sec)
此时查询table1中的记录如下,只剩一条数据了~
mysql> select * from table1;
+---+---+---+
| a | b | c |
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
1 row in set (0.00 sec)
(老板:插入前10w数据,插入5w数据后还剩8w数据??,咱们家数据让你喂狗了吗!!)
REPLACE INTO语法回顾:如果插入行出现唯一索引或者主键重复时,则delete老记录,而录入新的记录;如果不会导致唯一索引或者主键重复时,就直接添加新行。
我们可以看到,在用REPLACE INTO时每个唯一索引都会有影响的,可能会造成误删数据的情况,因此建议不要在多唯一索引的表中使用REPLACE INTO;
相关推荐
- Python字符串终极指南!单引号、双引号、三引号区别全解析
-
导语:Python中字符串(str)是最核心的数据类型!无论你是输出"HelloWorld"还是处理用户数据,都离不开它。今天彻底讲清字符串的三大定义方式及其核心区别,新手必看!...
- python 字符串的定义和表示_python字符串的用法
-
在Python中,字符串是一序列字符的集合。定义一个字符串可以使用单引号或双引号括起来的字符序列。...
- 简单的python-熟悉字符串相关的操作
-
str.py:#-*-coding:utf-8-*-#测试函数deff():#字符串使用单引号定义s1='test'print(s...
- Python初学者:3招搞定长字符串逐行读取,代码超简单
-
刚学Python的小伙伴,是不是遇到过这种尴尬情况?拿到一段老长的多行字符串——比如从文档里复制的日志、一段带换行的文章,想一行一行处理,如果直接打印全堆在一起,手动切又怕漏行,咋整啊?别慌!今天就给...
- Python 字符串_python字符串型怎么表达
-
除了数字,Python还可以操作字符串。字符串的形式是单引号('......')双引号(''.........'')或三个单引号(''&...
- 贴身口语第二关:请求帮忙、道歉、指路、接受礼物
-
02-@askforhelp请求协助1.F:Excuseme.Canyouhelpme?M:Yes,whatcanIdoforyou?...
- NBA赛季盘点之九大装逼&炫技时刻:“歪嘴战神”希罗领衔
-
欢迎大家来到直播吧NBA赛季盘点,历经许多波折,2019-20赛季耗时整整一年才圆满收官。魔幻的一年里有太多的时刻值得我们去铭记,赛场上更是不乏球员们炫技与宣泄情绪的装逼时刻,本期盘点就让我们来回顾一...
- 一手TTS-2语音合成模型安装教程及实际使用
-
语音合成正从云端调用走向本地部署,TTS-2模型作为开源语音生成方案之一,正在被越来越多开发者尝试落地。本篇文章从环境配置到推理调用,详尽拆解TTS-2的安装流程与使用技巧,为语音产品开发者提供...
- 网友晒出身边的巨人 普通人站一旁秒变“霍比特人”
-
当巨人遇到霍比特人,结果就是“最萌身高差”。近日网友们晒出了身边的巨人,和他们站在一起,普通人都变成了“霍比特人”。CanYouTellWho'sRelated?TheDutchGiant...
- 分手后我们还能做朋友吗?_分手后我们还能做朋友吗
-
Fewrelationshipquestionsareaspolarizingaswhetherornotyoushouldstayfriendswithanex.A...
- 如何用C语言实现Shellcode Loader
-
0x01前言之前github找了一个基于go的loader,生成后文件大小6M多,而且细节不够了解,一旦被杀,都不知道改哪里,想来还是要自己写一个loader...
- 微星Z490如何装Windows10系统以及怎么设 BIOS
-
小晨儿今天给大家讲一下msi微星Z490重怎样装系统以及怎么设置BIOS。一、安装前的准备工作1、一、安装前的准备工作1、备份硬盘所有重要的文件(注:GPT分区转化MBR分区时数据会丢失)2...
- 超实用!互联网软件开发人员不可不知的 Git 常用操作命令
-
在互联网软件开发的协作场景中,Git是不可或缺的版本控制工具。掌握其核心命令,能让代码管理效率大幅提升。本文精选Git高频实用命令,结合场景化说明,助你快速上手。仓库初始化与克隆...
- AI项目的持续集成持续部署实践_ai 项目
-
在独立开发AI工具的过程中,笔者逐步实践了一套高效的软件项目持续集成与持续部署(CI/CD)流程。这套流程以Git、GitHub和Vercel为核心,实现了从代码提交到生产环境上线的全链路自动化。这篇...
- 总结几个常用的Git命令的使用方法
-
1、Git的使用越来越广泛现在很多的公司或者机构都在使用Git进行项目和代码的托管,Git有它自身的优势,很多人也喜欢使用Git。...
- 一周热门
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)