mysql not exists 效率高_mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录

语句一:select count(*) from A where A.a not in (select a from B)

语句二:select count(*) from A left join B on A.a = B.a where B.a is null

语句三:select count(*) from A where not exists (select a from B where A.a = B.a)

知道以上三条语句的实际效果是相同的已经很久了,但是一直没有深究其间的效率对比。一直感觉上语句二是最快的。 今天工作上因为要对一个数千万行数据的库进行数据清除,需要删掉两千多万行数据。大量的用到了以上三条语句所要实现的功能。本来用的是语句一,但是结果是执行速度1个小时32分,日志文件占用21GB。时间上虽然可以接受,但是对硬盘空间的占用确是个问题。因此将所有的语句一都换成语句二。本以为会更快。没想到执行40多分钟后,第一批50000行都没有删掉,反而让SQL SERVER崩溃掉了,结果令人诧异。试了试单独执行这条语句,查询近一千万行的表,语句一用了4秒,语句二却用了18秒,差距很大。语句三的效率与语句一接近。

第二种写法是大忌,应该尽量避免。第一种和第三种写法本质上几乎一样。

假设buffer pool足够大,写法二相对于写法一来说存在以下几点不足: (1)left join本身更耗资源(需要更多资源来处理产生的中间结果集) (2)left join的中间结果集的规模不会比表A小 (3)写法二还需要对left join产生的中间结果做is null的条件筛选,而写法一则在两个集合join的同时完成了筛选,这部分开销是额外的

这三点综合起来,在处理海量数据时就会产生比较明显的区别(主要是内存和CPU上的开销)。我怀疑楼主在测试时buffer pool可能已经处于饱和状态,这样的话,写法二的那些额外开销不得不借助磁盘上的虚拟内存,在SQL Server做换页时,由于涉及到较慢的I/O操作因此这种差距会更加明显。

关于日志文件过大,这也是正常的,因为删除的记录多嘛。可以根据数据库的用途考虑将恢复模型设为simple,或者在删除结束后将日志truncate掉并把文件shrink下来。

因为以前曾经作过一个对这个库进行无条件删除的脚本,就是要删除数据量较大的表中的所有数据,但是因为客户要求,不能使用truncate table,怕破坏已有的库结构。所以只能用delete删,当时也遇到了日志文件过大的问题,当时采用的方法是分批删除,在SQL2K中用set rowcount @chunk,在SQL2K5中用delete top @chunk。这样的操作不仅使删除时间大大减少,而且让日志量大大减少,只增长了1G左右。 但是这次清除数据的工作需要加上条件,就是delete A from A where ....后面有条件的。再次使用分批删除的方法,却已经没效果了。 不知您知不知道这是为什么。

mysql not in 和 left join 效率问题记录

首先说明该条sql的功能是查询集合a不在集合b的数据。 not in的写法

select add_tb.RUID

from (select distinct RUID

from UserMsg

where SubjectID =12

and CreateTime>'2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00'

) add_tb

where add_tb.RUID

not in (select distinct RUID

from UserMsg

where SubjectID =12

and CreateTime

)

返回444行记录用时 0.07sec explain 结果

+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows |

Extra |

+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 452 |

Using where |

| 3 | DEPENDENT SUBQUERY | UserMsg | index_subquery | RUID,SubjectID,CreateTime | RUID | 96 | func | 2 |

Using index; Using where |

| 2 | DERIVED | UserMsg | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1857 |

Using where; Using temporary |

+----+--------------------+------------+----------------+---------------------------+------------+---------+------+------+--

----------------------------+

分析:该条查询速度快原因为id=2的sql查询出来的结果比较少,所以id=1sql所以运行速度比较快,id=2的使用了临时表,不知道这个时候是否使用索引? 其中一种left join

select a.ruid,b.ruid

from(select distinct RUID

from UserMsg

where SubjectID =12

and CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00'

) a left join (

select distinct RUID

from UserMsg

where SubjectID =12 and CreateTime< '2009-8-14 15:30:00'

) b on a.ruid = b.ruid

where b.ruid is null

返回444行记录用时 0.39sec

explain 结果

+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 452 |

|

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 1112 | Using where; Not exists

|

| 3 | DERIVED | UserMsg | ref | SubjectID,CreateTime | SubjectID | 5 | | 6667 | Using where; Using

temporary |

| 2 | DERIVED | UserMsg | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1838 | Using where; Using

temporary |

+----+-------------+------------+-------+----------------------+------------+---------+------+------+-----------------------

-------+

分析:使用了两个临时表,并且两个临时表做了笛卡尔积,导致不能使用索引并且数据量很大

另外一种left join

复制代码 代码如下:

select distinct a.RUID

from UserMsg a

left join UserMsg b

on a.ruid = b.ruid

and b.subjectID =12 and b.createTime < '2009-8-14 15:30:00'

where a.subjectID =12

and a.createTime >= '2009-8-14 15:30:00'

and a.createtime <='2009-8-17 16:00:00'

and b.ruid is null;

返回444行记录用时 0.07sec

explain 结果

+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+

| 1 | SIMPLE | a | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1839 | Using where;

Using temporary |

| 1 | SIMPLE | b | ref | RUID,SubjectID,CreateTime | RUID | 96 | dream.a.RUID | 2 | Using where;

Not exists; Distinct |

+----+-------------+-------+-------+---------------------------+------------+---------+--------------+------+---------------

--------------------+

分析:两次查询都是用上了索引,并且查询时同时进行的,所以查询效率应该很高

使用not exists的sql

复制代码 代码如下:

select distinct a.ruid

from UserMsg a

where a.subjectID =12

and a.createTime >= '2009-8-14 15:30:00'

and a.createTime <='2009-8-17 16:00:00'

and not exists (

select distinct RUID

from UserMsg

where subjectID =12 and createTime < '2009-8-14 15:30:00'

and ruid=a.ruid

)

返回444行记录用时 0.08sec

explain 结果

+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+

| 1 | PRIMARY | a | range | SubjectID,CreateTime | CreateTime | 9 | NULL | 1839 | Using

where; Using temporary |

| 2 | DEPENDENT SUBQUERY | UserMsg | ref | RUID,SubjectID,CreateTime | RUID | 96 | dream.a.RUID | 2 | Using

where |

+----+--------------------+---------+-------+---------------------------+------------+---------+--------------+------+------

------------------------+

分析:同上基本上是一样的,只是分解了2个查询顺序执行,查询效率低于第3个

为了验证数据查询效率,将上述查询中的subjectID =12的限制条件去掉,结果统计查询时间如下

0.20s

21.31s

0.25s

0.43s

laserhe帮忙分析问题总结

复制代码 代码如下:

select a.ruid,b.ruid

from( select distinct RUID

from UserMsg

where CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00'

) a left join UserMsg b

on a.ruid = b.ruid

and b.createTime < '2009-8-14 15:30:00'

where b.ruid is null;

执行时间0.13s

+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 1248 |

|

| 1 | PRIMARY | b | ref | RUID,CreateTime | RUID | 96 | a.RUID | 2 | Using where; Not exists

|

| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3553 | Using where; Using

temporary |

+----+-------------+------------+-------+-----------------+------------+---------+--------+------+--------------------------

----+

执行效率类似与not in的效率

数据库优化的基本原则:让笛卡尔积发生在尽可能小的集合之间,mysql在join的时候可以直接通过索引来扫描,而嵌入到子查询里头,查询规

划器就不晓得用合适的索引了。

一个SQL在数据库里是这么优化的:首先SQL会分析成一堆分析树,一个树状数据结构,然后在这个数据结构里,查询规划器会查找有没有合适

的索引,然后根据具体情况做一个排列组合,然后计算这个排列组合中的每一种的开销(类似explain的输出的计算机可读版本),然后比较里

面开销最小的,选取并执行之。那么:

explain select a.ruid,b.ruid from(select distinct RUID from UserMsg where CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00' ) a left join UserMsg b on a.ruid = b.ruid and b.createTime < '2009-8-14 15:30:00'

where b.ruid is null;

explain select add_tb.RUID

-> from (select distinct RUID

-> from UserMsg

-> where CreateTime>'2009-8-14 15:30:00'

-> and CreateTime<='2009-8-17 16:00:00'

-> ) add_tb

-> where add_tb.RUID

-> not in (select distinct RUID

-> from UserMsg

-> where CreateTime

-> );

explain

+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 1248 | Using where

|

| 3 | DEPENDENT SUBQUERY | UserMsg | index_subquery | RUID,CreateTime | RUID | 96 | func | 2 | Using index;

Using where |

| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3509 | Using where;

Using temporary |

+----+--------------------+------------+----------------+-----------------+------------+---------+------+------+------------

------------------+

开销是完全一样的,开销可以从 rows 那个字段得出(基本上是rows那个字段各个行的数值的乘积,也就是笛卡尔积)

但是呢:下面这个:

explain select a.ruid,b.ruid from(select distinct RUID from UserMsg where CreateTime >= '2009-8-14 15:30:00'

and CreateTime<='2009-8-17 16:00:00' ) a left join ( select distinct RUID from UserMsg where createTime < '2009-8-14

15:30:00' ) b on a.ruid = b.ruid where b.ruid is null;

执行时间21.31s

+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

|

+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 1248 |

|

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 30308 | Using where; Not exists

|

| 3 | DERIVED | UserMsg | ALL | CreateTime | NULL | NULL | NULL | 69366 | Using where; Using temporary

|

| 2 | DERIVED | UserMsg | range | CreateTime | CreateTime | 9 | NULL | 3510 | Using where; Using temporary

|

+----+-------------+------------+-------+---------------+------------+---------+------+-------+-----------------------------

-+

我就有些不明白

为何是四行

并且中间两行巨大无比

按理说

查询规划器应该能把这个查询优化得跟前面的两个一样的

(至少在我熟悉的pgsql数据库里我有信心是一样的)

但mysql里头不是

所以我感觉查询规划器里头可能还是糙了点

我前面说过优化的基本原则就是,让笛卡尔积发生在尽可能小的集合之间

那么上面最后一种写法至少没有违反这个原则

虽然b 表因为符合条件的非常多,基本上不会用索引

但是并不应该妨碍查询优化器看到外面的join on条件,从而和前面两个SQL一样,选取主键进行join

不过我前面说过查询规划器的作用

理论上来讲

遍历一遍所有可能,计算一下开销

是合理的

我感觉这里最后一种写法没有遍历完整所有可能

可能的原因是子查询的实现还是比较简单?

子查询对数据库的确是个挑战

因为基本都是递归的东西

所以在这个环节有点毛病并不奇怪

其实你仔细想想,最后一种写法无非是我们第一种写法的一个变种,关键在表b的where 条件放在哪里

放在里面,就不会用索引去join

放在外面就会

这个本身就是排列组合的一个可能

详细出处参考:http://www.jb51.net/article/29122.htm

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

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

相关文章

POJ 3981(字符串替换)

字符串替换Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 7290 Accepted: 3451Description 编写一个C程序实现将字符串中的所有"you"替换成"we"Input 输入包含多行数据 每行数据是一个字符串&#xff0c;长度不超过1000 数据以EOF结束Output…

.NET Core 基于 Grafana Loki 日志初体验

介绍Loki: like Prometheus, but for logs.Loki是一个轻量级的日志系统&#xff0c;受到Prometheus项目的启发&#xff0c;由Grafana团队设计和开发&#xff0c;所以在Grafana中是原生支持的&#xff0c;具有可水平扩展&#xff0c;高度可用等特性&#xff0c;通过存储压缩的、…

mysql80重置密码_MySQL8.0修改密码问题

MySQL5.7和之前的用户修改密码方式&#xff1a;mysql -uroot -e "Set passwordpassword(‘123’);"mysql -uroot -p123.com -e "use mysql;update user set authentication_stringpassword(456) where userroot;"update mysql.user set authentication_str…

基于开源流程引擎Activiti5的工作流开发平台BPMX3

2019独角兽企业重金招聘Python工程师标准>>> BPMX3平台是宏天软件在ESTBPM2的基础上&#xff0c;追随开源工作流平台Activiti5&#xff0c;由原班开发团队&#xff0c;历时一年&#xff0c;现重新推出一套解决中国政府及企业的业务流程的开发平台。 相对商业的工作流…

通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定...

如果说Actor是dapr有状态服务的内部体现的话&#xff0c;那绑定应该是dapr对serverless这部分的体现了。我们可以通过绑定极大的扩展应用的能力&#xff0c;甚至未来会成为serverless的基础。最开始接触dapr的时候&#xff0c;会在其官方首页看到这么一句话“Dapr is a portabl…

三位一体,用游戏打通孩子记忆力、认知和双语启蒙的学前神器

对于孩子学习知识&#xff0c;现在父母多表现有2个极端&#xff0c;一种完全不让小小孩学硬知识&#xff0c;一种又希望孩子从很小开始就学硬知识。小木比较反对在孩子6岁前就给他们生硬地灌输知识&#xff0c;一定得认识多少个字&#xff0c;背多少个单词&#xff0c;但只要做…

mysql在线快速修改密码_MySQL修改密码的几种方式

这篇文章主要介绍了MySQL修改密码的几种方式&#xff0c;帮助大家更好的理解和使用MySQL&#xff0c;感兴趣的朋友可以了解下前言&#xff1a;在日常使用数据库的过程中&#xff0c;难免会遇到需要修改账号密码的情景&#xff0c;比如密码太简单需要修改、密码过期需要修改、忘…

分享一个CSS3的网格系统架构 - ResponsiveAeon

日期&#xff1a;2012-7-30 来源&#xff1a;GBin1.com 在线演示 本地下载 曾经介绍过其它类型的CSS3网格系统&#xff0c;今天我们介绍一款能够帮助你快速创建基于HTML5/CSS3的响应式布局框架 - ResponsiveAeon。 它拥有一个宽度为1104px并且基于12个列的网格框架系统&#…

网络协议,没有想象中那么难

十个人程序员里面&#xff0c;有十个都会说自己学过网络协议&#xff0c;九个人都会说自己懂网络协议。但是面试的时候&#xff0c;问几个问题&#xff0c;能回答的可能只有两三个。不信&#xff1f;来&#xff0c;我问你几道。1、TCP 协议跟 UDP 协议有什么区别&#xff1f;你…

Mysql实现幂等_阿里面试官:接口的幂等性怎么设计?

大家好&#xff0c;我是狂聊。自己最近负责的几个接口&#xff0c;都涉及到了幂等性的操作&#xff0c;抽空总结了一下&#xff0c;这也是面试官比较爱问的问题。一、什么是幂等?看一下维基百科怎么说的&#xff1a;幂等性&#xff1a;多次调用方法或者接口不会改变业务状态&a…

谷歌搜索揭示人性最黑暗的秘密

全世界只有3.14 % 的人关注了数据与算法之美《卫报》网站发布文章指出&#xff0c;我们能够从我们在网上问的问题获得对自己更多的了解呢。美国数据科学家塞斯斯蒂芬斯-大卫多维茨&#xff08;Seth Stephens-Davidowitz&#xff09;通过分析谷歌的匿名搜索数据&#xff0c;揭示…

杂集

为什么80%的码农都做不了架构师&#xff1f;>>> 符号相关快捷键: 千分符号&#xff1a;alt 0137 (小键盘) 回车符号&#xff1a;alt 10&#xff08;小键盘&#xff09; 查看目录结构&#xff1a;进入windows concole&#xff0c;执行命令tree 转载于:https://my.o…

接口管理平台YApi

介绍YApi 是高效、易用、功能强大的 api 管理平台&#xff0c;旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API&#xff0c;YApi 还为用户提供了优秀的交互体验&#xff0c;开发人员只需利用平台提供的接口数据写入工具以及简单的…

saiku 连接 MySQL_Saiku连接mysql数据库(二)

Saiku连接Mysql数据库展示数据参考链接&#xff1a;https://www.cnblogs.com/shirui/p/8573491.html前提&#xff1a;Saiku已安装好&#xff0c;mysql已安装好1.添加Saiku的数据库驱动&#xff1a; mysql-connect-java-5.1.17.jar下载相应的数据库驱动放到 saiku-server\tomcat…

TreeView控件应用--访问文件或文件夹(一)

C#用TreeView访问文件或文件夹&#xff0c;通过递归&#xff0c;展开所有文件夹&#xff08;类似资源管理器的树形窗体&#xff09; 首先&#xff0c;算法是用递归算法&#xff0c;不断的递归文件。以此来遍历整个电脑的磁盘内容&#xff0c;过程也很简单。这种算法的时间复杂度…

凭自己本事单的身是一种怎样的体验?你根本配不上如此优秀的我!

全世界只有3.14 % 的人关注了数据与算法之美8月12号&#xff0c;微博网友烂剧斗士发了这么一条微博&#xff0c;称“看脱口秀大会这个哥没把我给笑死”。一位从事IT&#xff08;黑客&#xff09;的小哥哥&#xff0c;是这样回忆自己的求爱经历的↓↓#她根本配不上我这么聪明的男…

gather torch_浅谈Pytorch中的torch.gather函数的含义

pytorch中的gather函数pytorch比tensorflow更加编程友好&#xff0c;所以准备用pytorch试着做最近要做的一些实验。立个flag开始学习pytorch&#xff0c;新开一个分类整理学习pytorch中的一些踩到的泥坑。今天刚开始接触&#xff0c;读了一下documentation&#xff0c;写一个一…

WPF 右下角弹窗的简单实现

软件中经常出现右下角弹窗&#xff0c;从下面缓缓弹出的&#xff0c;这次就做个简陋的实现&#xff0c;思路就是在窗口加载和关闭时执行动画DoubleAnimation今天懒得做界面了&#xff0c;只实现了功能。看看效果&#xff1a;下面看看代码&#xff1a;主窗口添加一个按钮 &#…

你周围需要这6种人(文摘)

几乎没有什么不可思议的产品是一个人就能完成的。你需要其他人来帮助你&#xff0c;你也需要去帮助别人。在一个好的团队中&#xff0c;都需要哪种类型的人进驻&#xff1f;来自Forbes的Jessica Hagy告诉我们&#xff0c;你的周围需要这6种人&#xff1a;1. “怂恿者”&#xf…

今年不容易,要懂得爱护自己

冬天到了&#xff0c;衣服逐层加厚&#xff0c;脖子老是皱巴巴的&#xff0c;坐在位置上老是觉得周身不舒服&#xff0c;小木提醒下大家该爱护一下自己的颈椎。这个像缩成一团的东西&#xff0c;是什么&#xff1f;打开以后&#xff0c;它就变成个旅行枕啦&#xff0c;就是我们…