某天的工作是修复某个项目的bug,接着就发现,其sql极其混乱,有非常多的left join和in操作,还有嵌套查询(只有一个表的嵌套查询)。不知道看到过哪里的资料说,嵌套查询速度慢,于是我把全部嵌套查询都改成join的形式,嵌 套查询里面的where条件,我都写到join...on后面去了。突然一个想法冒出来:筛选条件跟在join...on后面 和 跟在整个sql语句最后面的where后面有什么区别呢?还有嵌套查询真的慢么?于是便有下面的测试产生,数据库环境为MS SQL 2005

一,inner join

  先看看非嵌套查询


非嵌套inner join

a.select * from t1
inner join t2 on t1.id = t2.id
inner join t3 on t1.id = t3.id
where t1.a=1 and t2.b=1 and t3.c=1 b.select * from t1
inner join t2 on t1.id = t2.id and t2.b=1 
inner join t3 on t1.id = t3.id
where t1.a=1 and t3.c=1 c.select * from t1
inner join t2 on t1.id = t2.id and t2.b=1 
inner join t3 on t1.id = t3.id and t3.c=1
where t1.a=1

  在上面三个非嵌套查询,让“and t2.b=1”和“and t3.c=1”分别在join...on和where之间游走,用Management Studio选中“包含实际的执行计划”并执行这三条语句,都得出下面这个执行计划。

  三个“聚集索引扫描”的谓词从上到下分别是:

    1.t3.c=1

    2.t1.a=1 (seek谓词:t1.id=t3.id)

    3.t2.b=1 (seek谓词:t2.id=t3.id)

  故可以认为:在MS SQL2005中,条件跟在inner join...on后面 和 跟在where后面是等价的。

  接着看嵌套查询


嵌套inner join

d.select * from t1
inner join (select * from t2 where t2.b=1)a on t1.id=a.id
inner join t3 on t1.id = t3.id
where t1.a=1 and t3.c=1 e.select * from t1
inner join (select * from t2 where t2.b=1)a on t1.id=a.id
inner join (select * from t3 where t3.c=1)b on t1.id=b.id
where t1.a=1 f.elect * from t1
inner join (select t3.id,t2.b,t3.c from t3 inner join t2 on t2.id = t3.id where t2.b=1 and t3.c=1)a on t1.id=a.id
where t1.a=1

  第一句sql语句把t2的查询变成子查询,第二句sql语句把t2,t3分别变成子查询,第三句把t2和t3的查询合成一个子查询,再看看实际的执行计划:

  跟上面非嵌套查询的执行计划一模一样。

  故可以认为:简单(注意是简单的,复杂的情况得另外考虑)嵌套查询和其相对应的非嵌套查询形式,执行效率是一样的(网上一些文章指出这是MS SQL优化器针对这些嵌套查询进行了优化)。

--------------------------------------------------------------------------------------------

  接着,在上面两个执行计划的图中又发现一个小问题,为什么明明是select t1 inner join t2 inner join
t3,执行计划却把t1和t3先inner join(t1.id = t3.id)再跟t2 inner join(t2.id =
t3.id)起来?

  经过三个表,四个表,五个表进行连接测试,发现这些顺序都是不确定的。很可能这些顺序是根据SQL优化器内的算法所决定的,由于没有源代码,所以无从考究。

(感谢Keep Walking的补充:

“可以指定顺序,force order选项,和keep plan选项
数量级,索引,统计的不同都可以导致顺序变化”。
我Google了一下option force order和option keep plan,发现SQL优化器做了很多事情,在这文章就不列出来了,大家有兴趣可以Goo一下。)

----------------------------------------

PS1:

  1.经测试,在inner join on后面t1.id = t2.id与t2.id = t1.id等价

  如果发现这文章有错误,欢迎指出。

----------------------------------------------------

PS2:经过songmc指出,发现了一些问题。补充对left join的测试。
二,left join

  附上songmc的代码:


Code

DECLARE @a TABLE(id INT IDENTITY,NAME VARCHAR(20)) 
INSERT INTO @a SELECT 'aa' 
UNION ALL SELECT 'bb' 
UNION ALL SELECT 'cc' 
UNION ALL SELECT 'dd'  DECLARE @b TABLE(id INT IDENTITY,NAME VARCHAR(20)) 
INSERT INTO @b SELECT 'ee' 
UNION ALL SELECT 'ff' 
UNION ALL SELECT 'gg' 
UNION ALL SELECT 'hh'  DECLARE @c TABLE (id INT IDENTITY,NAME VARCHAR(20)) 
INSERT INTO @c SELECT 'ii' 
UNION ALL SELECT 'jj' 
UNION ALL SELECT 'kk' 
UNION ALL SELECT 'll'  --a. 
SELECT 

FROM @a a 
LEFT JOIN @b b ON a.id = b.id 
LEFT JOIN @c c ON b.id = c.id 
WHERE a.NAME != 'aa'  --b. 
SELECT 

FROM @a a 
LEFT JOIN @b b ON a.id = b.id 
LEFT JOIN @c c ON b.id = c.id AND 
a.NAME != 'aa'

  在这里,把a.NAME != 'aa' 放在where后面以及放在c的on后面,查询出来的结果:

  发现sql语句b比sql语句a的结果多了一行记录,这里c.id和c.name为NULL,到底为什么呢?再做一个实验:


--c.

SELECT
*
FROM @a a
LEFT JOIN @b b ON a.id = b.id AND
a.[NAME] != 'aa'
LEFT JOIN @c c ON b.id = c.id

  这次把条件放到b后面了,看b和c的查询结果的对比图:

  发现不仅是c.id和c.name,连b.id和b.name为NULL,这是为什么呢?
      首先要理解left jion就是去掉不符合条件的,保留左表的行。

  

  分析sql语句b中的 “LEFT JOIN @c c ON b.id = c.id AND a.NAME != 'aa'”,表c和x(x为表a和表b连结后的中间结果)连结后的中间结果x1中,去掉a.NAME不等于'aa'的行,并保留左表,因此c.id和c.name为NULL。

  分析sql语句c中的 “LEFT JOIN @b b ON
b.id = c.id AND a.NAME !=
'aa'”,表b和表a连结后的中间结果x2中,去掉a.NAME不等于'aa'的行,并保留左表,因此b.id和b.name为NULL。又因为
“LEFT JOIN @c c ON b.id = c.id”,b.id为NULL,保留左表,所以c.id和c.name为NULL。

  inner join和left join不一样,inner join左边或右边的结果为空,该行记录就不显示了。而left join会以左边的表为保留表,就算右边的结果为空,该行仍然显示。

于是得出的结论是:条件放在on与放在where后面的作用是不一样的。on对中间结果进行筛选,再由where对最终结果进行筛选。

PS3:下面是Left Join on+where的执行过程(附上songmc给我的文档的精华部分,根据《Inside Microsoft® SQL Server™ 2005 T-SQL Querying》进行基于自己理解的修改)

sql:

SELECT * 
FROM a 
LEFT JOIN b ON a.id = b.id AND b.Name != 'ff'
WHERE a.NAME != 'aa' 

步骤1:FROM后面的两个表a,b进行笛卡尔积,生成虚拟表VT1。

步骤2:应用ON筛选器到VT1,只有条件(当前的条件为a.id = b.id AND b.Name != 'ff')为真的行,插入到VT2。

步骤3:添加外部行(OUTER (join))
  这一步只对OUTER JOIN起作用,如果是LEFT JOIN会以左边的表为保留表,如果是RIGHT JOIN会以右边的表为保留表。所谓外部行是指,保留表中的行。即使第二步的ON过滤掉了一些行,在这一步,会根据保留表添加第二步过滤掉的行,并生成VT3。

步骤4.应用WHERE筛选器到VT3,只有条件(当前是Name != ‘aa’)为真的行,插入到VT4。

SQL简单嵌套查询与非嵌套查询的比较(MSSQL2005)的更多相关文章

  1. mysql的缓冲查询和非缓冲查询

    最近在开发一个PHP程序时遇到了下面的错误: PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted 错误信息显示允许的 ...

  2. ElasticSearch的高级复杂查询:非聚合查询和聚合查询

    一.非聚合复杂查询(这儿展示了非聚合复杂查询的常用流程) 查询条件QueryBuilder的构建方法 1.1 精确查询(必须完全匹配上,相当于SQL语句中的“=”) ① 单个匹配 termQuery ...

  3. oracle 相关查询和非相关查询,oracle 去除重复数据,以及oracle的分页查询!

    一.oracle中的相关查询?和非相关查询? 二.oracle去除重复数据 1. 2. 3.oracle 实现分页? 利用rownum的唯一性,和子查询,将rownum从伪列变成实际列!

  4. php 非缓冲查询

    最近在开发一个PHP程序时遇到了下面的错误: PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted 错误信息显示允许的 ...

  5. Solr4.8.0源码分析(6)之非排序查询

    Solr4.8.0源码分析(6)之非排序查询 上篇文章简单介绍了Solr的查询流程,本文开始将详细介绍下查询的细节.查询主要分为排序查询和非排序查询,由于两者走的是两个分支,所以本文先介绍下非排序的查 ...

  6. (24)ASP.NET Core EF查询(查询的工作原理、跟踪与非跟踪查询)

    1.查询生命周期 在进入正题时候,我们先来了解EF Core查询的生命周期. 1.1LINQ查询会由Entity Framework Core处理并生成给数据库提供程序可处理的表示形式(说白了就是生成 ...

  7. [SQL SERVER系列]之嵌套子查询和相关子查询

    子查询有两种类型,一种是只返回一个单值的子查询,这时它可以用在一个单值可以使用的地方,这时子查询可以看作是一个拥有返回值的函数:另外一种是返回一列值的子查询,这时子查询可以看作是一个在内存中临时存在的 ...

  8. SQL嵌套子查询和相关子查询的执行过程有什么区别(推荐)

    SQLServer子查询可以分为 相关子查询 和 嵌套子查询 两类.前提, 假设Books表如下: 类编号 图书名 出版社 价格 ----------------------------------- ...

  9. ylb:子查询(嵌套子查询)和子查询(相关子查询)

    ylbtech-SQL Server:SQL Server-子查询(嵌套子查询)和子查询(相关子查询) SQL Server 子查询(嵌套子查询)和子查询(相关子查询). 1,ylb:1,子查询(嵌套 ...

随机推荐

  1. ResourceLoader笔记

    Ant路径匹配 Ant路径通配符支持“?”.“*”.“**”,注意通配符匹配不包括目录分隔符“/”: “?”:匹配一个字符,如“config?.xml”将匹配“config1.xml”: “*”:匹配 ...

  2. 20145240《Java程序设计》课程总结

    20145240<Java程序设计>课程总结 每周读书笔记链接汇总 20145240 <Java程序设计>第一周学习总结:http://www.cnblogs.com/2014 ...

  3. JQuery 操作 iframe

    JQuery访问iframe内的元素 $("iframe#Main", top.document).contents().find("#id"); JQuery ...

  4. 实现HTML格式的数据报表邮件

  5. poj2528线段树解题报告,离散化+线段树

    题目网址:http://poj.org/problem?id=2528 题意: n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=1 ...

  6. NCBI

    RefSeq: NCBI Reference Sequences GeneBank序列注释说明 利用NCBI查找基因信息 NCBI中RefSeq各种accession说明(一) NCBI中RefSeq ...

  7. Variation calling and annotation

    Resequencing 302 wild and cultivated accessions identifies genes related to domestication and improv ...

  8. 软件体系结构C2风格

    首先C2风格是最常用的一种软件体系结构风格.(下图为一个C2风格的架构图) C2是一种基于构件和消息的架构风格,可用于创建灵活的.可伸缩的软件系统.一个C2构架可以看成是按照一定规则由连接件连接的许多 ...

  9. 域名注册中EAP期间是什么意思

    所谓域名申请期间的EAP指的是,域名优先注册期,行业上也称为“早期接入期”,这个期间的时间是由该域名所在的管理注册局定,而这个EPA期的时间长度也不一样,有的是一个星期,也有的长达两个星期. 域名EA ...

  10. php提前输出响应及注意问题

    1.浏览器和服务器之间是通过HTTP进行通信的,浏览器发送请求给服务器,服务器处理完请求后,发送响应结果给浏览器,浏览器展示给用户.如果服务器处理请求时间比较长,那么浏览器就需要等待服务器的处理结果. ...