SQL Server2005 T-SQL 概览系列之一 逻辑查询处理
逻辑查询中的各个阶段
本节介绍逻辑查询所涉及的各个阶段。先描述每个阶段,然后再结合示例详细介绍。
逻辑查询处理的步骤序号
(8) SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY ALL <group_by_list>
(6) WITH {CUBE | ROLLUP}
(7) HAVING <having_condition>
(10) ORDER BY <order_by_list>
<join_type>:LEFT OUTER JOIN、RIGHT OUTER JOIN、INNER JOIN、FULL OUTER JOIN
SQL 不同于其他编程语言的最明显的特征是处理代码的顺序。在大多数编程语言中,代码是顺着处理的,但在SQL中,第一个被处理的子句是FROM子句。
整个T—SQL的逻辑查询处理过程可以简单的理解成:查询过程中生成了一个虚拟的表(VT),在查询完成前VT对于调用者是不可见的。每个步骤所产生的VT都作为下一个步骤的输入,知道最后一步执行完成后,会把其中包含的数据返回给调用者。在整个过程中可分为两类操作:(1)向VT中填充数据,(2)从VT中删除不符合条件的数据。
逻辑查询处理阶段简介
1、 FROM:对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1
2、ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2
3、OUTER(JOIN):如果指定了OUTER JOIN 保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。如果FROM子句中包含两个以上的表,则对上一个联接生成的表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
4、WHERE:对VT3应用WHERE筛选器。只用使<where_condition>为TRUE的行才被插入VT4.
5、GROUP BY:按GROUP BY 子句中的列列表对VT4中的行分组,生成VT5.
6、CUBE|ROLLUP:把超组插入VT5,生成VT6.
7、Having:对VT6应用HAVING筛选器。只有使<having_condition>为TRUE的组才会被插入VT7.
8、SELECT:处理SELECT列表,产生VT8.
9、DISTINCT:将重复的行从VT8中移除,产生VT9.
10、ORDER BY:将VT9中的行按 ORDER BY 子句中的列列表排序,生成一个游标(VC10).
11、从VC10的开始处选择指定数量或比例的行,生成VT11,并返回给调用者。
Customers/Orders场景下的示例查询
代码清单1-2 表Customers和Orders的数据定义语言和示例数据
2use tempdb
3go
4if object_id('dbo.orders') is not null
5 drop table dbo.orders
6go
7
8if object_id('dbo.customers') is not null
9 drop table dbo.customers
10go
11
12create table dbo.customers
13(
14 customerid char(5) not null primary key,
15 city nvarchar(10) not null
16);
17insert into dbo.customers (customerid,city) values ('FISSA','Madrid');
18insert into dbo.customers (customerid,city) values ('FRNDO','Madrid');
19insert into dbo.customers (customerid,city) values ('KRLOS','Madrid');
20insert into dbo.customers (customerid,city) values ('MRPHS','Ziobn');
21
22create table dbo.orders
23(
24 orderid int not null primary key,
25 customerid char(5) null references customers(customerid)
26);
27insert into dbo.orders (orderid,customerid) values (1,'FRNDO');
28insert into dbo.orders (orderid,customerid) values (2,'FRNDO');
29insert into dbo.orders (orderid,customerid) values (3,'KRLOS');
30insert into dbo.orders (orderid,customerid) values (4,'KRLOS');
31insert into dbo.orders (orderid,customerid) values (5,'KRLOS');
32insert into dbo.orders (orderid,customerid) values (6,'MRPHS');
33insert into dbo.orders (orderid,customerid) values (7,null);
34
35
代码清单1-3 查询:来自Madrid且订单数少于3的消费者
2from customers as c left outer join dbo.orders as o
3on c.customerid = o.customerid
4where c.city='Madrid'
5group by c.customerid
6having count(o.orderid)<3
7order by numbers
查询结果
customerid numbers
FISSA 0
FRNDO 2
逻辑查询步骤详解
步骤1 执行笛卡尔积(交叉联接)
该步骤相当于执行如下语句:select c.*,o.* from customers as c, orders as o
步骤2:应用ON筛选器(联接条件)
ON筛选器是可以用于查询的三个筛选器(ON、WHERE和HAVING)中的一个。ON筛选器中的逻辑表达式被应用到上一步骤返回的虚拟表(VT1)中的所有行。只有使<链接条件>为TRUE的那些行才会包含在由步骤2返回的虚拟表(VT2)中。
注:这里涉及到了三值逻辑(TRUE、FALSE和UNKNOWN),稍后章节中会详细讲述。
步骤2返回的虚拟表(VT2)
Match? C.customerid C.city O.orderid O.customerid
TRUE FRNDO Madrid 1 FRNDO
TRUE FRNDO Madrid 2 FRNDO
TRUE KRLOS Madrid 3 KRLOS
TRUE KRLOS Madrid 4 KRLOS
TRUE KRLOS Madrid 5 KRLOS
TRUE MRPHS Zion 6 MRPHS
步骤3: 添加外部行
这一步只与外部链接有关。通过指定一种外部联接(LEFT、RIGHT或FULL),可以把一个或两个输入表标记为保留表。把一个表标记为保留表表示你希望返回该表的所有行,即使<联接条件>过滤掉了一些行。左外部联接把左表标记为保留表,右外部联接把右表标记为保留表,完全外部联接把两个表都标记为保留表。步骤3返回VT2中的行以及保留表在步骤2被过滤掉的行。保留表中的行被称为外部行。外部行中非保留表中的属性被赋值为NULL。最后生成虚拟表VT3
在这个示例中,保留表是customers:
步骤3返回的虚拟表VT3
C.customerid C.city O.orderid O.customerid
FRNDO Madrid 1 FRNDO
FRNDO Madrid 2 FRNDO
KRLOS Madrid 3 KRLOS
KRLOS Madrid 4 KRLOS
KRLOS Madrid 5 KRLOS
MRPHS Zion 6 MRPHS
FISSA Madrid NULL NULL
步骤4:应用WHERE筛选器
对上一步返回的虚拟表中的所有行应用WHERE筛选器。只有符合<where_condition>的行才会成为该步骤所返回虚拟表的一部分。
注:关于逻辑筛选条件到底是用在ON中还是用在WHERE中在下一章节中详细描述。
步骤4 返回的虚拟表 VT4
C.customerid C.city O.orderid O.customerid
FRNDO Madrid 1 FRNDO
FRNDO Madrid 2 FRNDO
KRLOS Madrid 3 KRLOS
KRLOS Madrid 4 KRLOS
KRLOS Madrid 5 KRLOS
FISSA Madrid NULL NULL
步骤5:分组
上一步返回的行被分配到各个组。GROUP BY 子句中列列表的每一个唯一值组合成为一组。上一步返回的每个基行都被分配到一个组,且仅分配到一个组。得到虚拟表VT5。VT5由两部分组成:实际组构成的成组部分(group section)和由上一步返回的基行构成的原始部分(raw section)
步骤5得到的VT5
Groups Raw
C.customerid C.customerid C.city O.orderid O.customerid
FRNDO FRNDO Madrid 1 FRNDO
FRNDO Madrid 2 FRNDO
KRLOS KRLOS Madrid 3 KRLOS
KRLOS Madrid 4 KRLOS
KRLOS Madrid 5 KRLOS
FISSA FISSA Madrid NULL NULL
步骤6:应用CUBE或ROLLUP选项
如果指定了CUBE或ROLLUP,将创建超组并把它添加到上一步返回的虚拟表中,生成虚拟表VT6
步骤7:应用HAVING筛选器
对上一步返回的组应用HAVING筛选器。只有符合<having_condition>的组才会成为这一步骤返回的虚拟表(VT6)的一部分。
步骤7返回的虚拟表VT7
C.customerid C.customerid C.city O.orderid O.customerid
FRNDO FRNDO Madrid 1 FRNDO
FRNDO Madrid 2 FRNDO
FISSA FISSA Madrid NULL NULL
步骤8:处理select列表
尽管SELECT列表是查询中最先被指定的,但却被放到第8步处理。处理SELECT列表这一步用于构建最终要被返回给调用方的表。SELECT列表中的表达式可以是上一步返回的虚拟表的基列,也可以是对这些基列的操作。
步骤8返回的虚拟表VT8
C.customerid numorders
FRNDO 2
FISSA 0
步骤9:应用DISTINCT子句
如果在查询中指定了DISTINCT子句,将从上一步返回的虚拟表中移除重复的行,并生成虚拟表VT9。
步骤10:应用ORDER BY 子句
按照ORDER BY 子句中的列列表排序上一步骤返回的行,返回游标VC10.
步骤11:应用TOP选项
TOP选项允许你指定要返回的行数或百分比。从游标(VC10)的最前面选择指定的行数,生成VT11并返回给调用者。
下一章节详细介绍TOP和确定性