SQLServer中SELECT语句的实行各种

SQL
不同于与其他编程语言的最明显特征是处理代码的顺序。在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后被处理。

今天在写一条语句的时候,在查询分析器里边执行
要用10s,换用另外一种写法只用少于1s的时间,同事说是因为Sql句语执行顺序的原因。之前看过一点相
关的书,有一点印象,到网上找了资料,学习下。
逻辑查询处理步骤

      每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回
给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。下面是对应用于SQL
server 2000和SQL Server 2005的各个逻辑步骤的简单描述。

复制代码 代码如下:

 

(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)ORDER BY <order_by_list>

图片 1

每个步骤产生一个虚拟表,该虚拟表被用作下一个步骤的输入。只有最后一步生成的表返回给调用者。如
果没有某一子句,则跳过相应的步骤。

(8)SELECT (9)DISTINCT  (11)<Top Num> <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)ORDER BY <order_by_list>

  1. FROM:对FROM子句中的前两个表执行笛卡尔积,生成虚拟表VT1。
    2.
    ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2。
  2. OUTER(JOIN):如果指定了OUTER
    JOIN,保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。
    如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到
    处理完所有的表为止。
    4.
    对VT3应用WHERE筛选器。只有使<where_condition>为TRUE的行才被插入VT4。
  3. GROUP BY:按GROUP BY 子句中的列列表对VT4中的行分组,生成VT5。
  4. CUBE|ROLLUP:把超组插入VT5,生成VT6。
    7.
    HAVING:对VT6应用HAVING筛选器。只有使<having_condition>为TRUE的组才会被插入VT7。
  5. SELECT:处理SELECT列表,产生VT8。
  6. DISTINCT:将重复的行从VT8中移除,产生VT9。
  7. ORDER BY:将VT9中的行按ORDER
    BY子句中的列列表排序,生成一个有表(VC10)。
    11.
    TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者。
    以下是其它网友的补充:
    好像自已在书写 SQL 语句时由于不清楚各个关键字的执行顺序, 往往组织的 SQL
    语句缺少很好的逻辑, 凭感觉 “拼凑” ( 不好意思, 如果您的 SQL 语句也经常
    “拼凑”, 那您是不是得好好反省一下呢?, 呵呵).
    这样做确实是爽了自己, 可苦了机器, 服务器还需要在我们的杂乱无章的 SQL
    语句中寻找它下一句需要执行的关键字在哪里.
    效率嘛, 由于我们的感觉神经对秒以下的变化实在不敏感, 暂且就认为自已写的
    SQL 顺序无关紧要, “反正没什么变化!”, 呵呵.其实服务器对每句 SQL
    解析时间都会有详细记录的, 大家可以看一下自已按习惯写的 SQL
    和按标准顺序写的SQL解析时间差别有多大.
    因此, 建议大家在平时工作中 SQL 语句按标准顺序写, 一是专业, 二是实用,
    呵呵, 不过我觉得最主要的是心里感觉舒服.
    标准的 SQL 的解析顺序为:
    (1).FROM 子句, 组装来自不同数据源的数据
    (2).WHERE 子句, 基于指定的条件对记录进行筛选
    (3).GROUP BY 子句, 将数据划分为多个分组
    (4).使用聚合函数进行计算
    (5).使用 HAVING 子句筛选分组
    (6).计算所有的表达式
    (7).使用 ORDER BY 对结果集进行排序
    举例说明: 在学生成绩表中 (暂记为 tb_Grade), 把
    “考生姓名”内容不为空的记录按照 “考生姓名” 分组, 并且筛选分组结果, 选出
    “总成绩” 大于 600 分的.
    标准顺序的 SQL 语句为:
    select 考生姓名, max(总成绩) as max总成绩
    from tb_Grade
    where 考生姓名 is not null
    group by 考生姓名
    having max(总成绩) > 600
    order by max总成绩
    在上面的示例中 SQL 语句的执行顺序如下:
    (1). 首先执行 FROM 子句, 从 tb_Grade 表组装数据源的数据
    (2). 执行 WHERE 子句, 筛选 tb_Grade 表中所有数据不为 NULL 的数据
    (3). 执行 GROUP BY 子句, 把 tb_Grade 表按 “学生姓名” 列进行分组
    (4). 计算 max() 聚集函数, 按 “总成绩” 求出总成绩中最大的一些数值
    (5). 执行 HAVING 子句, 筛选课程的总成绩大于 600 分的.
    (7). 执行 ORDER BY 子句, 把最后的结果按 “Max 成绩” 进行排序.
    好了,看了这些之后,我相信大家都知道了SQL中select语句的执行顺序了吧!哈哈!

图片 2

您可能感兴趣的文章:

  • SQL
    Select语句完整的执行顺序
  • T-SQL
    查询语句的执行顺序解析
  • SQL语句执行顺序图文介绍
  • SQL语句执行顺序详解

逻辑查询处理阶段简介

  1. FROM:对FROM子句中的前两个表执行笛卡尔积(Cartesian
    product)(交叉联接),生成虚拟表VT1
  2. ON:对VT1应用ON筛选器。只有那些使为真的行才被插入VT2。
  3. OUTER(JOIN):如 果指定了OUTER JOIN(相对于CROSS JOIN 或(INNER
    JOIN),保留表(preserved
    table:左外部联接把左表标记为保留表,右外部联接把右表标记为保留表,完全外部联接把两个表都标记为保留表)中未找到匹配的行将作为外部行添加到
    VT2,生成VT3.如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
  4. WHERE:对VT3应用WHERE筛选器。只有使为true的行才被插入VT4.
  5. GROUP BY:按GROUP BY子句中的列列表对VT4中的行分组,生成VT5.
  6. CUBE|ROLLUP:把超组(Suppergroups)插入VT5,生成VT6.
  7. HAVING:对VT6应用HAVING筛选器。只有使为true的组才会被插入VT7.
  8. SELECT:处理SELECT列表,产生VT8.
  9. DISTINCT:将重复的行从VT8中移除,产生VT9.
  10. ORDER BY:将VT9中的行按ORDER BY
    子句中的列列表排序,生成游标(VC10).
  11. TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回调用者。

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

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

select * 
from(select orderid,customerid from orders order by orderid) 
as d

下面的视图也会产生错误

create view my_view
as

select *
from orders
order by orderid

      在SQL中,表表达式中不允许使用带有ORDER
BY子句的查询,而在T—SQL中却有一个例外(应用TOP选项)。

      所以要记住,不要为表中的行假设任何特定的顺序。换句话说,除非你确定要有序行,否则不要指定ORDER
BY 子句。排序是需要成本的,SQL
Server需要执行有序索引扫描或使用排序运行符。
      推荐一段SQL代码:行列转置
      

图片 3
图片 4 
图片 5
图片 6create table tb(姓名 varchar(10),课程 varchar(10),分数 int)
图片 7insert into tb values(‘张三’ , ‘语文’ , 74)
图片 8insert into tb values(‘张三’ , ‘数学’ , 83)
图片 9insert into tb values(‘张三’ , ‘物理’ , 93)
图片 10insert into tb values(‘李四’ , ‘语文’ , 74)
图片 11insert into tb values(‘李四’ , ‘数学’ , 84)
图片 12insert into tb values(‘李四’ , ‘物理’ , 94)
图片 13go 
图片 14
图片 15–SQL SERVER 2000 静态SQL,指课程只有语文、数学、物理这三门课程。(以下同)
图片 16select 姓名 as 姓名 ,
图片 17  max(case 课程 when ‘语文’ then 分数 else 0 end) 语文,
图片 18  max(case 课程 when ‘数学’ then 分数 else 0 end) 数学,
图片 19  max(case 课程 when ‘物理’ then 分数 else 0 end) 物理
图片 20from tb
图片 21group by 姓名

====================================================================================================

好像自已在书写 SQL 语句时由于不清楚各个关键字的执行顺序, 往往组织的 SQL
语句缺少很好的逻辑, 凭感觉 “拼凑” ( 不好意思, 如果您的 SQL 语句也经常
“拼凑”, 那您是不是得好好反省一下呢?, 呵呵). 
这样做确实是爽了自己, 可苦了机器, 服务器还需要在我们的杂乱无章的 SQL
语句中寻找它下一句需要执行的关键字在哪里. 
效率嘛, 由于我们的感觉神经对秒以下的变化实在不敏感, 暂且就认为自已写的
SQL 顺序无关紧要, “反正没什么变化!”, 呵呵.其实服务器对每句 SQL
解析时间都会有详细记录的, 大家可以看一下自已按习惯写的 SQL
和按标准顺序写的SQL解析时间差别有多大. 
因此, 建议大家在平时工作中 SQL 语句按标准顺序写, 一是专业, 二是实用,
呵呵, 不过我觉得最主要的是心里感觉舒服. 
标准的 SQL 的解析顺序为: 
(1).FROM 子句, 组装来自不同数据源的数据 
(2).WHERE 子句, 基于指定的条件对记录进行筛选 
(3).GROUP BY 子句, 将数据划分为多个分组 
(4).使用聚合函数进行计算 
(5).使用 HAVING 子句筛选分组 
(6).计算所有的表达式 
(7).使用 ORDER BY 对结果集进行排序 
举例说明: 在学生成绩表中 (暂记为 tb_Grade), 把
“考生姓名”内容不为空的记录按照 “考生姓名” 分组, 并且筛选分组结果, 选出
“总成绩” 大于 600 分的. 
标准顺序的 SQL 语句为: 
select 考生姓名, max(总成绩) as max总成绩 
from tb_Grade 
where 考生姓名 is not null 
group by 考生姓名 
having max(总成绩) > 600 
order by max总成绩 
在上面的示例中 SQL 语句的执行顺序如下: 
(1). 首先执行 FROM 子句, 从 tb_Grade 表组装数据源的数据 
(2). 执行 WHERE 子句, 筛选 tb_Grade 表中所有数据不为 NULL 的数据 
(3). 执行 GROUP BY 子句, 把 tb_Grade 表按 “学生姓名” 列进行分组 
(4). 计算 max() 聚集函数, 按 “总成绩” 求出总成绩中最大的一些数值 
(5). 执行 HAVING 子句, 筛选课程的总成绩大于 600 分的. 
(7). 执行 ORDER BY 子句, 把最后的结果按 “Max 成绩” 进行排序.

=====================================================================================================

SQLServer2005中查询语句的执行顺序

 

–1.from
–2.on
–3.outer(join)
–4.where
–5.group by
–6.cube|rollup
–7.having
–8.select
–9.distinct
–10.order by
–11.top

  1. 逻辑查询处理步骤序号
    (8)SELECT (9)DISTINCT (11)
    (1)FROM
    (3)  JOIN
    (2)    ON
    (4)WHERE
    (5)GROUP BY
    (6)WITH {CUBE | ROLLUP}
    (7)HAVING
    (10)ORDER BY

 

每个步骤产生一个虚拟表,该虚拟表被用作下一个步骤的输入。
只有最后一步生成的表返回给调用者。
如果没有某一子句,则跳过相应的步骤。

  1. FROM:
       对FROM子句中的前两个表执行笛卡尔积,生成虚拟表VT1。
  2. ON:
       对VT1应用ON筛选器。只有那些使为真的行才被插入VT2。
  3. OUTER(JOIN):
       如果指定了OUTER
    JOIN,保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。
       如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
  4. 对VT3应用WHERE筛选器。只有使为TRUE的行才被插入VT4。
  5. GROUP BY:
       按GROUP BY 子句中的列列表对VT4中的行分组,生成VT5。
  6. CUBE|ROLLUP:
       把超组插入VT5,生成VT6。
  7. HAVING:
       对VT6应用HAVING筛选器。
       只有使为TRUE的组才会被插入VT7。

   注:having不能单独使用,having子句是对分组后的记录的筛选,所以有having必须要有group
by

  1. SELECT:
       处理SELECT列表,产生VT8。
  2. DISTINCT:
       将重复的行从VT8中移除,产生VT9。
  3. ORDER BY:
       将VT9中的行按ORDER BY子句中的列列表排序,生成一个有表(VC10)。
    11.
    TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者。

   注:top n可以实现分页

        select top 20 * from
雇员                                                                 ——第一页

 

        select top 20 * from 雇员

        where 身份证号码 not in (select top 20 身份证号码 from
雇员)            ——第二页

 

  1. 准备数据

SET NOCOUNT ON;USE tempdb;GO IF OBJECT_ID('dbo.Orders') IS NOT NULL DROP TABLE dbo.Orders;GOIF OBJECT_ID('dbo.Customers') IS NOT NULL DROP TABLE dbo.Customers;GO CREATE TABLE dbo.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 dbo.Orders( orderid    INT     NOT NULL PRIMARY KEY, customerid CHAR(5) NULL     REFERENCES Customers(customerid)); INSERT INTO dbo.Orders(orderid, customerid) VALUES(1,'FRNDO');INSERT INTO dbo.Orders(orderid, customerid) VALUES(2,'FRNDO');INSERT INTO dbo.Orders(orderid, customerid) VALUES(3,'KRLOS');INSERT INTO dbo.Orders(orderid, customerid) VALUES(4,'KRLOS');INSERT INTO dbo.Orders(orderid, customerid) VALUES(5,'KRLOS');INSERT INTO dbo.Orders(orderid, customerid) VALUES(6,'MRPHS');INSERT INTO dbo.Orders(orderid, customerid) VALUES(7, NULL);

  执行结果:

图片 22

图片 23

  1. 查询语句

USE tempdb;GOSELECT C.customerid, COUNT(O.orderid) AS numordersFROM dbo.Customers AS C LEFT OUTER JOIN dbo.Orders AS O    ON C.customerid = O.customeridWHERE C.city = 'Madrid'GROUP BY C.customeridHAVING COUNT(O.orderid) < 3ORDER BY numorders;

  执行结果:

图片 24

  1. 逻辑查询处理步骤详解

 

1.
执行笛卡尔乘积,形成VT1。如果左表包含n行,右表包含m行,VT1将包含n×m行。

    执行结果VT1:

图片 25

 

  1. 应用ON 筛选器,只有为TRUE的那些行才会包含在VT2中。

ON C.customerid = O.customerid

  三值逻辑:

      TRUE、FALSE、UNKNOWN为SQL中逻辑表达式的可能值。

  UNKNOWN值通常出现在含NULL值的逻辑表达式中,如NULL > 42; NULL =
NULL; X + NULL > Y。

  NOT TRUE 等于 FALSE

  NOT FALSE 等于TRUE

  NOT UNKNOWN 等于 UNKNOWN

  所有的查询筛选器,如ON、WHERE、HAVING把UNKNOWN看作为FALSE处理。

  CHECK约束中的UNKNOWN值被当作TRUE对待。如果表中含有一个CHECK约束,要求salary列的值必须大于0,则插入salary为NULL的行时可以被接受。

  UNIQUE约束、排序操作、分组操作认为两个NULL值是相等的。如,表中有一列定义了UNIQUE约束,则无法向表中插入该列值为NULL的两行。GROUP
BY子句把所有NULL值分在一组。ORDERB BY子句把所有NULL值排列在一起。

  对VT1增加ON筛选器的结果VT2:

图片 26

  

  1. 添加外部行,通过指定LEFT、RIGHT、FULL中的一种OUTER
    JOIN,可以把左表、右表、所有表标记为保留表。把一个表设为保留表表示返回该表的所有行,即使已经执行过筛选。保留表中的这些行被称为外部行,外部行中非保留表的属性被赋予NULL,最后生成VT3:

图片 27

  

4.
应用WHERE筛选器,只有符合的行才会成为VT4的一部分。因为数据还没有被分组,所以不能使用聚合筛选器,例如WHERE
orderdate =
MAX(orderdate)。也不能饮用SELECT列表中的别名,因为SELECT列表这时还没有被处理,例如SELECT
YEAR(orderdate) AS orderyear WHERE orderyear > 2000。

  对于包含OUTER
JOIN子句的查询,如何判断到底是在ON筛选器还是在WHERE筛选器中指定逻辑表达式:ON在添加外部行前被应用,WHERE在外部行添加之后被应用。ON筛选器对保留表中部分行的一处不是最终的,因为还要执行添加外部行的步骤,而WHERE筛选器对这些行的移除是最终的。

  只有在使用外部联接时,ON和WHERE子句才会存在这种逻辑限制,当使用内部联接时,在那里指定逻辑表达式都无所谓,因为没有上面的步骤3。

WHERE C.city = 'Madrid'

  生成虚拟表VT4:

图片 28

  

  1. 分组。GROUP BY子句中列列表的每个唯一的值组合成为一组,生成VT5:

 

 

  Groups