先从一例子看join on 和 where执行结果的不同
CREATE TABLE "SCOTT"."A" ( "PERSON_ID" NUMBER(5) NULL , "PERSON_NAME" VARCHAR2(255 BYTE) NULL ) ; -- ---------------------------- -- Records of A -- ---------------------------- INSERT INTO "SCOTT"."A" VALUES ('1', '张三'); INSERT INTO "SCOTT"."A" VALUES ('2', '李四'); INSERT INTO "SCOTT"."A" VALUES ('3', '王五'); INSERT INTO "SCOTT"."A" VALUES ('4', '赵六'); INSERT INTO "SCOTT"."A" VALUES ('5', '周七'); CREATE TABLE "SCOTT"."B" ( "PERSON_ID" NUMBER(5) NULL , "LOVE_FRUIT" VARCHAR2(255 BYTE) NULL ); -- ---------------------------- -- Records of B -- ---------------------------- INSERT INTO "SCOTT"."B" VALUES ('1', '香蕉'); INSERT INTO "SCOTT"."B" VALUES ('2', '苹果'); INSERT INTO "SCOTT"."B" VALUES ('3', '橘子'); INSERT INTO "SCOTT"."B" VALUES ('4', '梨'); INSERT INTO "SCOTT"."B" VALUES ('8', '桃');
查询语句1
SELECT * FROM A LEFT JOIN ORACLE.B ON A.PERSON_ID=B.PERSON_ID AND A.PERSON_ID=1;
查询语句2
SELECT * FROM A LEFT JOIN ORACLE.B ON A.PERSON_ID=B.PERSON_ID WHERE A.PERSON_ID=1;
为什么结果不同呢? 可以从查询逻辑处理的过程解释。
select语句的处理过程
我们知道,SQL 查询的大致语法结构如下:
(5)SELECT DISTINCT TOP(<top_specification>) <select_list> (1)FROM <left_table> <join_type> JOIN <right_table> ON <on_predicate> (2)WHERE <where_predicate> (3)GROUP BY <group_by_specification> (4)HAVING <having_predicate> (6)ORDER BY <order_by_list>
select 语法的处理顺序
The following steps show the processing order for a SELECT statement.
- FROM
- ON
- JOIN
- WHERE
- GROUP BY
- WITH CUBE or WITH ROLLUP
- HAVING
- SELECT
- DISTINCT
- ORDER BY
- TOP
这些步骤执行时, 每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。
select各个阶级分别干了什么:
(1)FROM 阶段
FROM阶段标识出查询的来源表,并处理表运算符。在涉及到联接运算的查询中(各种join),主要有以下几个步骤:
a.求笛卡尔积。不论是什么类型的联接运算,首先都是执行交叉连接(cross join),求笛卡儿积,生成虚拟表VT1-J1。
b.ON筛选器。这个阶段对上个步骤生成的VT1-J1进行筛选,根据ON子句中出现的谓词进行筛选,让谓词取值为true的行通过了考验,插入到VT1-J2。
c.添加外部行。如果指定了outer join,还需要将VT1-J2中没有找到匹配的行,作为外部行添加到VT1-J2中,生成VT1-J3。
经过以上步骤,FROM阶段就完成了。概括地讲,FROM阶段就是进行预处理的,根据提供的运算符对语句中提到的各个表进行处理(除了join,还有apply,pivot,unpivot)
(2)WHERE阶段
WHERE阶段是根据<where_predicate>中条件对VT1中的行进行筛选,让条件成立的行才会插入到VT2中。
(3)GROUP BY阶段
GROUP阶段按照指定的列名列表,将VT2中的行进行分组,生成VT3。最后每个分组只有一行。
(4)HAVING阶段
该阶段根据HAVING子句中出现的谓词对VT3的分组进行筛选,并将符合条件的组插入到VT4中。
(5)SELECT阶段
这个阶段是投影的过程,处理SELECT子句提到的元素,产生VT5。这个步骤一般按下列顺序进行
a.计算SELECT列表中的表达式,生成VT5-1。
b.若有DISTINCT,则删除VT5-1中的重复行,生成VT5-2
c.若有TOP,则根据ORDER BY子句定义的逻辑顺序,从VT5-2中选择签名指定数量或者百分比的行,生成VT5-3
(6)ORDER BY阶段
根据ORDER BY子句中指定的列明列表,对VT5-3中的行,进行排序,生成游标VC6.
例子解释
查询语句1的执行过程
SELECT * FROM A LEFT JOIN ORACLE.B ON A.PERSON_ID=B.PERSON_ID AND A.PERSON_ID=1;
求笛卡尔积,产生5*5=25条记录
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
|
1 |
张三 |
1 |
香蕉 |
1 |
张三 |
2 |
苹果 |
1 |
张三 |
3 |
橘子 |
1 |
张三 |
4 |
梨 |
1 |
张三 |
8 |
桃 |
2 |
李四 |
1 |
香蕉 |
2 |
李四 |
2 |
苹果 |
2 |
李四 |
3 |
橘子 |
2 |
李四 |
4 |
梨 |
2 |
李四 |
8 |
桃 |
3 |
王五 |
1 |
香蕉 |
3 |
王五 |
2 |
苹果 |
3 |
王五 |
3 |
橘子 |
3 |
王五 |
4 |
梨 |
3 |
王五 |
8 |
桃 |
4 |
赵六 |
1 |
香蕉 |
4 |
赵六 |
2 |
苹果 |
4 |
赵六 |
3 |
橘子 |
4 |
赵六 |
4 |
梨 |
4 |
赵六 |
8 |
桃 |
5 |
周七 |
1 |
香蕉 |
5 |
周七 |
2 |
苹果 |
5 |
周七 |
3 |
橘子 |
5 |
周七 |
4 |
梨 |
5 |
周七 |
8 |
桃 |
ON筛选器(A.PERSON_ID=B.PERSON_ID AND A.PERSON_ID=1)
A.PERSON_ID |
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
1 |
张三 |
1 |
香蕉 |
添加外部行
A.PERSON_ID |
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
1 |
张三 |
1 |
香蕉 |
1 |
张三 |
|
|
1 |
张三 |
|
|
1 |
张三 |
|
|
1 |
张三 |
|
|
查询语句2的执行过程
SELECT * FROM A LEFT JOIN ORACLE.B ON A.PERSON_ID=B.PERSON_ID WHERE A.PERSON_ID=1;
求笛卡尔积,产生5*5=25条记录
A.PERSON_ID |
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
1 |
张三 |
1 |
香蕉 |
1 |
张三 |
2 |
苹果 |
1 |
张三 |
3 |
橘子 |
1 |
张三 |
4 |
梨 |
1 |
张三 |
8 |
桃 |
2 |
李四 |
1 |
香蕉 |
2 |
李四 |
2 |
苹果 |
2 |
李四 |
3 |
橘子 |
2 |
李四 |
4 |
梨 |
2 |
李四 |
8 |
桃 |
3 |
王五 |
1 |
香蕉 |
3 |
王五 |
2 |
苹果 |
3 |
王五 |
3 |
橘子 |
3 |
王五 |
4 |
梨 |
3 |
王五 |
8 |
桃 |
4 |
赵六 |
1 |
香蕉 |
4 |
赵六 |
2 |
苹果 |
4 |
赵六 |
3 |
橘子 |
4 |
赵六 |
4 |
梨 |
4 |
赵六 |
8 |
桃 |
5 |
周七 |
1 |
香蕉 |
5 |
周七 |
2 |
苹果 |
5 |
周七 |
3 |
橘子 |
5 |
周七 |
4 |
梨 |
5 |
周七 |
8 |
桃 |
ON筛选器 (A.PERSON_ID=B.PERSON_ID )
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
|
1 |
张三 |
1 |
香蕉 |
2 |
李四 |
2 |
苹果 |
3 |
王五 |
3 |
橘子 |
4 |
赵六 |
4 |
梨 |
添加外部行
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
|
1 |
张三 |
1 |
香蕉 |
2 |
李四 |
2 |
苹果 |
3 |
王五 |
3 |
橘子 |
4 |
赵六 |
4 |
梨 |
5 |
周七 |
|
|
WHERE阶段 (A.PERSON_ID=1)
A.PERSON_ID |
PERSON_NAME |
B.PERSON_ID |
LOVE_FRUIT |
1 |
张三 |
1 |
香蕉 |
有了上面的验证,我们可以猜测下面语句的执行结果
SELECT * FROM A LEFT JOIN ORACLE.B ON A.PERSON_ID=B.PERSON_ID
参考文献
从join on和where执行顺序认识T-SQL查询处理执行顺序
https://msdn.microsoft.com/en-us/library/ms189499%28v=SQL.100%29.aspx