SQL Server 2005技术一

本文深入解析SQL查询处理的各个阶段,从基本的JOIN操作到复杂的GROUP BY、HAVING、SELECT、DISTINCT和ORDER BY,直至最终的TOP筛选。详细介绍了每个阶段的功能、实现方式以及注意事项,帮助读者全面理解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 <Group_by_list>
(6) WITH {CUBE | ROLLUP}
(7) HAVING <having_condition>
(10) ORDERBY <order_by_list>

逻辑查询处理阶段简介
1.  FROM: 对FROM子句中的前两个表执行笛卡尔积(Cartesianproduct)(交叉连接) ,生成虚拟表VT1
2.  ON: 对VT1应用ON筛选器。只有那些使用<join_condition>为真的行才被插入VT2.
3.  OUTER(JOIN):如果指定了OUTER JOIN (相对于CROSS JOIN 或 INNER  JOIN),保留表(preserved  table)中未找到匹配的行将作为外部行添加到VT2,生成T3.如果FROM 子句包含两个以上的表,则对上一个连接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
4.  WHERE: 对VT3应用WHERE 筛选器。只有使<where_condition>为TRUE的行才能被插入VT4
5.  GROUP BY : 按GROUP BY  子句中的列列表对VT4中的行分组,生成VT5.
6.  CUBE|ROLLUP: 把超组(Supergroups)插入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.  TOP: 从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者。


案例如下:

IF OBJECT_ID('dbo.Orders') IS NOT NULL
DROP TABLE dbo.Orders
GO
IF OBJECT_ID('dbo.Customers') IS NOT NULL
DROP TABLE dbo.Customers
GO
CREATE TABLE Customers
(
customerid char(5) NOT NULL PRIMARY KEY,
city varchar(10) NOT NULL 
);
INSERT INTO dbo.Customers(customerid,city)values('FISSA','Madrid');
INSERT INTO dbo.Customers(customerid,city)values('FRNDO','Madrid');
INSERT INTO dbo.Customers(customerid,city)values('KRLOS','Madrid');
INSERT INTO dbo.Customers(customerid,city)values('MRPHS','Zion');
CREATE TABLE Orders
(
orderid INT NOT NULL PRIMARY KEY ,
customerid char(5) NULL REFERENCES Customers(customerid)
)
GO
INSERT INTO Orders(orderid ,customerid) values(1,'FRNDO');
INSERT INTO Orders(orderid ,customerid) values(2,'FRNDO');
INSERT INTO Orders(orderid ,customerid) values(3,'KRLOS');
INSERT INTO Orders(orderid ,customerid) values(4,'KRLOS');
INSERT INTO Orders(orderid ,customerid) values(5,'KRLOS');
INSERT INTO Orders(orderid ,customerid) values(6,'MRPHS');
INSERT INTO Orders(orderid ,customerid) values(7,NULL);

查询来自 Madrid ,且订单数小于3的消费者。

查询语句如下:

SELECT C.customerid,COUNT(O.orderid) numorders FROM
Customers AS C LEFT OUTER JOIN Orders AS O
ON C.customerid=O.customerid
WHERE C.city='Madrid'
GROUP BY C.customerid
HAVING COUNT(O.orderid)<3
ORDER BY numorders

步骤一,执行笛卡尔成绩(交叉连接)

               对FROM 子句的钱两个表执行笛卡尔乘积(交叉联接或未限定的联接),生成虚拟表VT1。VT1左表的行和右表的行的每一个可能的组合都包含一行。如果左表包含N行,右表包含M行,VT1将包含 N * M 行。


步骤二,使用ON筛选器(联接条件)

ON筛选器是用于查询的三个筛选器(ON,WHERE,HAVING)中的其中一个。ON筛选器的逻辑表达式被应用到上一步返回的虚拟表(VT1)中的所有行。只有使<联接条件>为TRUE的那些航才会包含在步骤2返回的虚拟表(VT2)中。

        ——————————————————————————————————————

        三值运算符: true,false,unknow 。

         true ,false 都比较好理解,unknow 比较特殊点。

         在筛选其中比较两个NULL值将得到UNKNOW,它会被当作FALSE处理,就好像其中一个NULL不等于另一个NULL。PS:怪不得写SQL的时候,用查询语句 WHERE的时候, =NULL是不行的,要 IS NULL 才是正确的写法。

          而UNIQUE约束,排序操作和分组操作认为两个NULL值是相等的。

          1.  如果表中有一列定义了UNIQUE约束,将无法向表中插入该列值为NULL的两行。

          2.GROUP BY 子句吧所有NULL值分到一组。

          3.ORDER BY子句把所有NULL值排列在一起。

步骤三,添加外部行

          这一步只与外部联接有关,通过指定外部联接(LEFT,RIGHT或FULL),可以把一个或两个输入表标记为保留表。如果把一个表标记为保留表表示你希望返回该表的所有行,即使<联接条件>过滤掉了一些行。左外联接(UT JLEFT OUT JOIN)把左表作为保留表,右外(RIGHT OUT JOIN)连接把右表作为保留表,完全外部联接(FULL OUT JOIN)吧两个表都作为保留表。


步骤四,应用WHERE 筛选器

          对上一步返回的虚拟表中的所有行应用WHERE筛选器。只有符合<where_condition>的行才会成为该步骤返回虚拟表<VT4>的一部分。

          警告:数据未分组,不能使用聚合筛选器,例如:WHERE orderdate=MAX(orderdate) 。也不能使用SELECT 列表中的别名,因为SELECT 这时还未被处理。

           对于OUTER JOIN 子句的查询,有 ON 和 WHERE 。应该在 WHERE 后面指定查询条件。因为在ON 之后,有的行还是会被添加进来,    而WHERE 是在步骤 三之后执行,所以,WHERE 的移除才是最终的。

          提示:在使用外部链接是,ON和WHERE子句才会存在这种逻辑差别。当使用内部联接时,在哪里指定表达式都无所谓,步骤三将被跳过。这些筛选器相继被应用,中间没有任何步骤。


 步骤五:分组
    如果在查询中指定了 GROUP BY 子句,则后面的所有步骤(HAVING、SELECT等)只能指定可以为成组得到标量值的表达式。换句话说,表达式的结果可以使 GROUP BY 列表中的列/表达式(如C.customerid)或聚合函数,如COUNT(O.orderid)。
  

步骤六:应用CUBE 或 ROLLUP选项

          如果制定了 CUBE 或 ROLLUP,讲创建超组并把它添加到上一步返回的虚拟表中,生成虚拟表VT6

 

步骤七:应用HAVING筛选器
    对上一步返回的组应用HAVING筛选器。只有符合<having_condition>组才会成为这一步骤返回的虚拟表(VT6)的一部分。HAVING是第一个也是唯一一个用到一份住数据的筛选器。

注意:在这里指定COUNT(O.orderid)而不是COUNT(*)是非常重要的。因为该联接时外部联接,没有订单的消费者将作为外部航添加到结果集。COUNT(*)会把外部航也统计在内,使得无法准确统计FISSA的订单数。而COUNT(O.orderid)则可以准确地统计消费者的订单数,为消费者FISSA生成期望的值0.与其他聚合函数一样,COUNT(<expression>)会忽略NULL值。



步骤八:处理SELECT列表
尽管SELECT列表是查询中最先被指定的,但却被放到第8步处理。处理SELECT列表这一步用于构建最终要被返回给调用方的表。SELECT列表中的表达式可以使由上一步返回的虚拟表的基列,也


可以是对这些激烈的操作。


重要:在SELECT 列表中创建的别名,不能再前面的步骤中使用。
小技巧:在大多数编程环境下,交换两个变量会用到临时变量。而在SQL中可以使用下面的语句:
UPDATE dbo.T1 SET C1=C2,C2=C1
逻辑上,你应该假设所有操作同时发生。就好像在整个操作完成前不会修改表,然后计算结果替换数据。因为类似的原因,下面这个UPDATE语句:
UPDATE dbo.T1 SET C1=C1+(SELECT MAX(C1) FROM dbo.T1) ;
将更新T1中所有行,为C1列加上开始时T1中最大的C1值。你不用担心最大的C1值会随着操作而持续变化,因为该操作会瞬间发生。


步骤九:应用DISTINCT子句
    如果在查询中制定了DISTINCT子句,将从上一步返回的虚拟表中移除重复航,病生成VT9.


步骤十:应用ORDER BY 子句
按ORDER BY 子句中的列列表排序上一步返回的行,返回游标VC10.这一步是第一步也是唯一一步可以使用SELECT列表中的列别名的步骤。
重要:这一步不同于其他步骤的是,它不返回有效的表,而是返回一个游标。SQL是基于集合理论的。集合不会预先对它的行排序,它只是成员的逻辑集合,成员的顺序无关紧要。对表进行排序的查询可以返回一个对象,包含按特定物理顺序组织的行。ANSI把这种对象称为游标。理解这一步是正确理解SQL的基础。(真正的游标和此处的“游标”区别在哪里?)

       因为步骤十不返回表(而是返回游标),使用了ORDER BY子句的查询不能用作表表达式。表表达式包括:试图、内联标志函数、子查询、派生表和共用表表达式(CTE)。它的结果必须返回给期望得到物理记录集的客户端应用程序。例如,下面的派生表查询无效,并产生一个错我:

       SELECT *  FROM ( SELECT orderId,customerid FROM dbo.Orders ORDER BY orderid ) AS D;

同样,下面的试图也无效:

CREATE VIEW dbo.VsortedOrders

AS

SELECT orderid,customerid FROM dbo.Orders ORDER BY orderid

GO

在SQL中,表表达式不允许使用带有ORDER BY子句的查询。

所以要记住,不要为表中的行假设任何特定的顺序。换句话说,除非你确实需要有序 行,否则不要指定ORDER BY子句。排序时需要成本的,SQL Server需要执行有序索引扫描(orderid indes scan)或使用排序运算符。

ORDER BY这一步认为两个NULL是相等的,所有的NULL在排序时会被排在一起。

步骤11:应用TOP选项
    TOP选项允许你指定要返回的行数或百分比(取整)。SQL 2000中 TOP的输入必须是一个常量,而在SQL Server2005中,输入可以使任何独立的表达式。从游标的最前面选择指定的行数,生成表VT11并返回给调用者。


















评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值