Mysql批量更新

看我如何将两小时的SQL语句优化到只需要2秒

Posted by Jason on May 19, 2025

我有一张订单表,订单表里面有百万条数据左右。

某一天,发现我们订单表里面缺失一些数据,需要补充这些数据上去。

补充的方法是,从Excel表格里面导入数据到数据库中。一导就是几万条的更新数据。

遇到这种需求,你会怎么做?

背景(创建一张订单表)

订单表,order_id不唯一

因为历史原因,除了ID唯一,其他都不唯一,所以没办法创建唯一索引。

CREATE TABLE `order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_id` bigint(30) NOT NULL DEFAULT '0' COMMENT '订单id',
  `order_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '下单时间',
  `finish_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '完成时间',
  `goods_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品id',
  `is_import` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否导入数据',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_order_id` (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='广告订单表';

order_id和goods_id属于同一组数据。所以更新条件就是order_id和goods_id

单条更新

因为需要导入的数据一个有几万条,最简单的方式就是直接写几万条update语句。

命名: /root/import.sql

UPDATE order SET finish_time='2025-04-30 22:44:15',is_import=1 WHERE order_id=312752276883 and goods_id='10123600267523';
UPDATE order SET finish_time='2025-04-30 22:25:27',is_import=1 WHERE order_id=3127334304 and goods_id='10114578974526';
UPDATE order SET finish_time='2025-04-30 22:06:19',is_import=1 WHERE order_id=313832333610 and goods_id='10139828706513';
UPDATE order SET finish_time='2025-04-30 20:05:41',is_import=1 WHERE order_id=3131442946843 and goods_id='100121866583';
UPDATE order SET finish_time='2025-04-30 19:48:32',is_import=1 WHERE order_id=313523412157 and goods_id='100108187176';

保存为文件,可以通过链接mysql source执行

mysql -h xxxxxx.com -u root -p
# 密码

# 选择你的数据库
source /root/import.sql

然后你就慢慢等待吧

批量更新

上面那个方式更新,每次会从本地读取一条sql语句到mysql服务器去执行。如果有几万条更新,那么就要读取几万次。几万个网络I/O,mysql服务器也需要解析几万次。

所以这种效率非常的低下。

优化思路就是,让mysql服务器可以批量处理,而不是一条一条处理。把我们需要更新的数据,提前告诉它,让它一次性更新。

这种方式专业名词叫什么我也不知道,好像是MySql Update Join吧。

原理就是把我们需要更新的数据插入一张临时表里面,然后再使用update join这样的语句,引用临时表里面的数据,直接就一次批量更新了。

创建一张临时表

-- 创建表(保存需要导入的数据)
CREATE TABLE `import_order` (
  `order_id` bigint(20) NOT NULL,
  `finish_time` datetime DEFAULT NULL,
  `goods_id` bigint(20) DEFAULT NULL
  KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 批量插入你要更新的数据
insert into import_order

批量更新语句

-- 更新数据
UPDATE order AS o
INNER JOIN import_order AS i ON o.order_id = i.order_id AND o.goods_id = i.goods_id 
SET o.finish_time = i.finish_time, o.is_import = 1

效果和上面的单条更新是一样的。但是效率却是质的提升。

其他批量更新语句参考

UPDATE Per
SET
	Per.PersonCityName=Addr.City,
	Per.PersonPostCode=Addr.PostCode
FROM Persons Per
INNER JOIN AddressList Addr ON Per.PersonId = Addr.PersonId
UPDATE Persons,AddressList
set 
	Persons.PersonCityName = AddressList.City,
	Persons.PersonPostCode=AddressList.PostCode
where Persons.PersonId = AddressList.PersonId

参考地址