将MySQL去重操作优化到极致之三弹连发
发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,将MySQL去重操作优化到极致之三弹连发(一):巧用索引与变量http://blog.csdn.net/wzy0623/article/details/54377986实验准备:MySQL 5.6.1
千家信息网最后更新 2026年11月02日将MySQL去重操作优化到极致之三弹连发
http://blog.csdn.net/wzy0623/article/details/54377986
实验准备:
MySQL 5.6.14
执行计划如下:

可以看到MySQL 给 t1表的item_id自动创建了一个索引.
2.使用MySQL特性
效率尚可,省时省力.
3.使用自定义变量
执行计划如下:

以上都是没有添加任何索引的情况.
添加索引如下:
create index idx_sort on t_source(created_time,item_name,item_id);
analyze table t_source;
创建索引之后,
使用表连接查询方式耗时11s,小幅提升.
使用MySQL特性的方式,耗时11-12s,反而更慢.
使用MySQL自定义变量的方式,耗时还是18s.
很显然,MySQL自定义变量的方式,其实没有利用索引.
最终改进SQL
耗时11s.
该语句具有以下特点。
(1)消除了嵌套子查询,只需要对t_source表进行一次全索引扫描,查询计划已达最优。
(2)无需distinct二次查重。
(3)变量判断与赋值只出现在where子句中。
(4)利用索引消除了filesort。
强制通过索引idx_sort查找数据行 -> 应用where筛选器 -> 处理select列表 -> 应用order by子句。
为了使变量能够按照created_time和item_name的排序顺序进行赋值和比较,必须按照索引顺序查找数据行。这里的force index (idx_sort)提示就起到了这个作用,必须这样写才能使整条查重语句成立。否则,因为先扫描表才处理排序,因此不能保证变量赋值的顺序,也就不能确保查询结果的正确性。order by子句同样不可忽略,否则即使有force index提示,MySQL也会使用全表扫描而不是全索引扫描,从而使结果错误。
索引同时保证了created_time,item_name的顺序,避免了文件排序。force index (idx_sort)提示和order by子句缺一不可,索引idx_sort在这里可谓恰到好处、一举两得。
查询语句开始前,先给变量初始化为数据中不可能出现的值,然后进入where子句从左向右判断。先比较变量和字段的值,再将本行created_time和item_name的值赋给变量,按created_time,item_name的顺序逐行处理。item_name是字符串类型,(@b:=item_name)不是有效的布尔表达式,因此要写成(@b:=item_name) is not null。
"insert into t_target select * from t_source group by created_time,item_name;"的写法,它受"sql_mode='ONLY_FULL_GROUP_BY'"的限制。
运行耗时和原文有出入,可能是因为我的环境是SSD的缘故.
另外,避免回表的开销,可以增加索引的字段
drop index idx_sort on t_source;
create index idx_sort on t_source(created_time,item_name,item_id,modified_time,other);
analyze table t_source;
使用上述索引,终极改进的SQL 耗时可以降到 9.5s
参考:
http://blog.csdn.net/wzy0623/article/details/54378367
http://blog.csdn.net/wzy0623/article/details/54378575
将MySQL去重操作优化到极致之三弹连发(一):巧用索引与变量
http://blog.csdn.net/wzy0623/article/details/54377986
实验准备:
MySQL 5.6.14
- create table t_source
- (
- item_id int,
- created_time datetime,
- modified_time datetime,
- item_name varchar(20),
- other varchar(20)
- );
- create table t_target like t_source;
- delimiter //
- create procedure sp_generate_data()
- begin
- set @i := 1;
- while @i<=500000 do
- set @created_time := date_add('2017-01-01',interval @i second);
- set @modified_time := @created_time;
- set @item_name := concat('a',@i);
- insert into t_source
- values (@i,@created_time,@modified_time,@item_name,'other');
- set @i:=@i+1;
- end while;
- commit;
- set @last_insert_id := 500000;
- insert into t_source
- select item_id + @last_insert_id,
- created_time,
- date_add(modified_time,interval @last_insert_id second),
- item_name,
- 'other'
- from t_source;
- commit;
- end
- //
- delimiter ;
- call sp_generate_data();
- insert into t_source
- select * from t_source where item_id=1;
- commit;
- select count(*),count(distinct created_time,item_name) from t_source;
- truncate t_target;
- insert into t_target
- select distinct t1.* from t_source t1,
- (select min(item_id) item_id,created_time,item_name from t_source t3 group by created_time,item_name) t2
- where t1.item_id = t2.item_id;
- commit;
执行计划如下:

可以看到MySQL 给 t1表的item_id自动创建了一个索引.
2.使用MySQL特性
- truncate t_target;
- insert into t_target
- select min(item_id),created_time,modified_time,item_name,other
- from t_source
- group by created_time,item_name;
- commit;
效率尚可,省时省力.
3.使用自定义变量
- set @a:='0000-00-00 00:00:00';
- set @b:=' ';
- set @f:=0;
- truncate t_target;
- insert into t_target
- select
- item_id, created_time, modified_time, item_name, other
- from
- (
- select
- t0 . *,
- if(@a = created_time and @b = item_name, @f:=0, @f:=1) f,
- @a:=created_time,
- @b:=item_name
- from
- (
- select
- *
- from
- t_source
- order by created_time , item_name
- ) t0
- ) t1
- where
- f = 1;
- commit;
执行计划如下:

以上都是没有添加任何索引的情况.
添加索引如下:
create index idx_sort on t_source(created_time,item_name,item_id);
analyze table t_source;
创建索引之后,
使用表连接查询方式耗时11s,小幅提升.
使用MySQL特性的方式,耗时11-12s,反而更慢.
使用MySQL自定义变量的方式,耗时还是18s.
很显然,MySQL自定义变量的方式,其实没有利用索引.
最终改进SQL
- set @a:='0000-00-00 00:00:00';
- set @b:=' ';
- truncate t_target;
- insert into t_target
- select * from t_source force index (idx_sort)
- where (@a!=created_time or @b!=item_name) and (@a:=created_time) is not null and (@b:=item_name) is not null
- order by created_time,item_name;
- commit;
耗时11s.
该语句具有以下特点。
(1)消除了嵌套子查询,只需要对t_source表进行一次全索引扫描,查询计划已达最优。
(2)无需distinct二次查重。
(3)变量判断与赋值只出现在where子句中。
(4)利用索引消除了filesort。
强制通过索引idx_sort查找数据行 -> 应用where筛选器 -> 处理select列表 -> 应用order by子句。
为了使变量能够按照created_time和item_name的排序顺序进行赋值和比较,必须按照索引顺序查找数据行。这里的force index (idx_sort)提示就起到了这个作用,必须这样写才能使整条查重语句成立。否则,因为先扫描表才处理排序,因此不能保证变量赋值的顺序,也就不能确保查询结果的正确性。order by子句同样不可忽略,否则即使有force index提示,MySQL也会使用全表扫描而不是全索引扫描,从而使结果错误。
索引同时保证了created_time,item_name的顺序,避免了文件排序。force index (idx_sort)提示和order by子句缺一不可,索引idx_sort在这里可谓恰到好处、一举两得。
查询语句开始前,先给变量初始化为数据中不可能出现的值,然后进入where子句从左向右判断。先比较变量和字段的值,再将本行created_time和item_name的值赋给变量,按created_time,item_name的顺序逐行处理。item_name是字符串类型,(@b:=item_name)不是有效的布尔表达式,因此要写成(@b:=item_name) is not null。
"insert into t_target select * from t_source group by created_time,item_name;"的写法,它受"sql_mode='ONLY_FULL_GROUP_BY'"的限制。
运行耗时和原文有出入,可能是因为我的环境是SSD的缘故.
另外,避免回表的开销,可以增加索引的字段
drop index idx_sort on t_source;
create index idx_sort on t_source(created_time,item_name,item_id,modified_time,other);
analyze table t_source;
使用上述索引,终极改进的SQL 耗时可以降到 9.5s
参考:
http://blog.csdn.net/wzy0623/article/details/54378367
http://blog.csdn.net/wzy0623/article/details/54378575
索引
变量
子句
方式
顺序
查询
数据
语句
处理
排序
提示
字段
特性
环境
结果
保证
应用
极致
有效
省时
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
工业互联网科技新基建
互联网下的科技发展
北京互联网科技公司
金融科技与互联网金融
互联网高科技企业
大白互联网科技
互联网科技咨询
国外互联网科技网站
佩信高科技互联网
科技大学互联网专业好不好
互联网公司会议桌创意科技软装饰
科技与互联网的利与弊
开一家科技互联网公司需要多少钱
链环科技传统企业怎么转型互联网
互联网科技人工智能实验室
浙南科技城互联网大厦建设工程
四川慕妮娅互联网科技有限公司
爱医互联网络科技
互联网硬科技公司
唐山双旺合互联网科技产业园
咖啡
互联网是第几次科技革命的成果
科技金融是不是互联网金融
没上市的科技互联网公司
麻工互联网科技
菲比寻常互联网科技
滨湖区互联网智能科技产品供应
洛阳昕诺飞互联网科技有限公司
如何创新互联网科技
张召忠谈中国互联网科技