您的位置:首页 > 数据库

SQL语句分析:ON与WHERE的比较_简单嵌套查询与非嵌套查询的比较

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

一,inner join

  先看看非嵌套查询

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。

  由于篇幅有限(其实是不想这篇文章太长了),就不把例子贴上了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: