关于T-SQL中exists或者notexists子查询的“伪优化”的做法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关于T-SQL中exists或者notexists⼦查询的“伪优化”的做法
问题起源
在使⽤t-sql中的exists(或者not exists)⼦查询的时候,不知道什么时候开始,发现⼀⼩部分⼈存在⼀种“伪优化”的⼀些做法,
并且向不明真相的群众传递这⼀种写法“优越性”,实在看不下去,
⽆法传递给他⼈正确的指导思想⽆可厚⾮,给他⼈传递错误的思想或者说误导⼈倒是⼀种罪恶。
本来这个事情是不值得⼀提的,看到越来越多被误导的群众开始推崇这种做法(甚⾄开始坚信了),实在是看不习惯,不吐不快。
典型的问题如下
select * from TableA a
where exists
(select 1 from TableB b where a.Id = b.Id ),当然这⾥⼦查询⾥写成select * 也⽆所谓。
这个要表达的逻辑就是说B表中存在与A表相同的Id的数据,就成⽴,这是要表达的逻辑。
参考如下写法,有⼈偏偏在在exists⼦查询中加上top 1 1,问其原因,为什么提⾼性能?理由就是加了top 1 1,只要在TableB中存在⼀条满⾜条件的条件即可,同时不⽤返回所有的⾏和列,因此可以提⾼性能。
select * from TableA a
where exists
(select top 1 1 from TableB b where a.Id = b.Id )
与直接写select 1 from TableB where a.Id =b.Id相⽐,真的可以提⾼性能吗?
exists(或者not exists)⼦查询的实现是⼀种半连接的“探测”逻辑机制(Semi Join),意思就是只要存在(⽽不关⼼具体有多少条)符合条件的数据即可,当然是不会再B表中找到所有的数据⾏(或者列)之后再返回。
但是exists(或者not exists)具体在执⾏的时候,到底⾛不⾛Semi Join不⼀定,跟具体的执⾏计划有关,本⽂暂不讨论⾛不⾛Semi Join的问题,只讨论⼦查询中select top 1 1 的写法到底影不影响效率。
测试验证
就以AdventureWorks2012⽰例库的两个表做demo,看看两者的执⾏计划和IO信息,会发现⼦查询中加不加 top 1,执⾏计划⼀样,IO⼀样,扯什么性能……
exists(或者not exists)的半连接的逻辑机制(Semi Join)决定了,你写不写top 1,它都是找到⼀个符合条件的数据之后就返回外层查询, 甚⾄在⼦查询中写select * from TableName,如果⾛Semi Join的执⾏⽅式,他照样是探测到有⼀条存在的数据之后就返回,肯定不会把所有的⾏都给找出来再返回,
以下截图可以看到,即便⼦查询是select * ,IO信息也是⼀样的(当然执⾏计划也⼀样)。
在当前这种情况下,可以认为exists⼦查询中的*,也是不会影响效率什么的。
甚⾄是可以在⼦查询中select⼀个常量,也不会影响到效率或者说改变执⾏计划。
总结
这个问题⽐较简答,当然这个场景也仅限于sqlserver中的exists或者not exists⼦查询,对于别的数据库也不确定是不是优化器内部会⾃动优化。
当前这种场景下,对于sqlserver来说,不要费尽⼼思去刻意⽤select top 1 1去“优化”了。