Sql 语句优化与执行顺序

SQL语句的执行顺序

SELECT语句的执行顺序大家比较少关注,下面将为您详细介绍SQL语句中SELECT语句的执行顺序,供您参考,希望对您能够有所帮助。

SELECT语句的执行的逻辑查询处理步骤:

(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. FROM:对FROM子句中的前两个表执行笛卡尔积,生成虚拟表VT1。

2. ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2。

3. OUTER(JOIN):如果指定了OUTER JOIN,保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。

如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到

处理完所有的表为止。

4. 对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. TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者。

 

今天上午在开发的过程中,突然遇到一个问题,需要了解SQL语句的执行顺序才能继续,上网上查了一下相关的资料,现整理如下:
一、sql语句的执行步骤:
 1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义。 
2) 语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限。 
3)视图转换,将涉及视图的查询语句转换为相应的对基表查询语句。 
4)表达式转换, 将复杂的 SQL 表达式转换为较简单的等效连接表达式。
 5)选择优化器,不同的优化器一般产生不同的“执行计划” 
6)选择连接方式, ORACLE 有三种连接方式,对多表连接 ORACLE 可选择适当的连接方式。 
7)选择连接顺序, 对多表连接 ORACLE 选择哪一对表先连接,选择这两表中哪个表做为源数据表。 
8)选择数据的搜索路径,根据以上条件选择合适的数据搜索路径,如是选用全表搜索还是利用索引或是其他的方式。 
9)运行“执行计划” 
二、oracle 共享原理:
        ORACLE将执行过的SQL语句存放在内存的共享池(shared buffer pool)中,可以被所有的数据库用户共享 当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的 执行路径.这个功能大大地提高了SQL的执行性能并节省了内存的使用 
三、oracle 语句提高查询效率的方法:1: where column in(select * from ... where ...); 2:... where exists (select 'X' from ...where ...); 第二种格式要远比第一种格式的效率高。在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询 使用EXIST,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间 Oracle系统在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中 避免使用having字句 避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销

SQL Select语句完整的执行顺序: 

1、from子句组装来自不同数据源的数据;
 2、where子句基于指定的条件对记录行进行筛选; 
3、group by子句将数据划分为多个分组; 
4、使用聚集函数进行计算;
5、使用having子句筛选分组; 
6、计算所有的表达式; 
7、使用order by对结果集进行排序。

我想请哪位高人分析一下以下几种SQL语句类型的执行顺序.SQL语句原型为; 
1.select 列列表 from 表列表名/视图列表名 where 条件. 
2.select 列列表 from 表列表名/视图列表名 where 条件 group by (列列表) having 条件 
3.select 列列表 from 表列表名/视图列表名 where 条件 group by (列列表) having 条件 order by 列列表 
4.select 列列表 from 表1 join 表2 on 表1.列1=表2.列1...join 表n  on 表n.列1=表(n-1).列1 where 表1.条件 and 表2.条件...表n.条件 
请问他们执行的时候顺序是怎样的呢?比如说第1条的意思是说:是先根据where条件从from表列表中选出满足条件的结果集,然后在从结果集中选出列列表中的列记录集 


1. 先where 后select 
2. 先where 再group 再having 后select 
3. 先where 再group 再having 再select 后order 
4. 先join  再where 后select 

第二条是这样的,如果你提取的字段名里边包涵有count,sum函数,必须用group by 分组,having是给函数加条件,就等于二次过滤. 
order 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 成绩" 进行排序.

  文章来源: http://www.cnblogs.com/ziyiFly/archive/2008/09/12/1289614.html

SQL Server 查询处理中的各个阶段(SQL执行顺序)

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

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

 

(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子句中的前两个表执行笛卡尔积(Cartesian product)(交叉联接),生成虚拟表VT1
  2. ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2。
  3. OUTER(JOIN):如 果指定了OUTER JOIN(相对于CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部联接把左表标记为保留表,右外部联接把右表标记为保留表,完全外部联接把两个表都标记为保留表)中未找到匹配的行将作为外部行添加到 VT2,生成VT3.如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
  4. WHERE:对VT3应用WHERE筛选器。只有使<where_condition>为true的行才被插入VT4.
  5. GROUP BY:按GROUP BY子句中的列列表对VT4中的行分组,生成VT5.
  6. CUBE|ROLLUP:把超组(Suppergroups)插入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,并返回调用者。

注:步骤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代码:行列转置
      


/*问题:假设有张学生成绩表(tb)如下:
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94

想变成(得到如下结果) 
姓名 语文 数学 物理 
---- ---- ---- ----
李四 74   84   94
张三 74   83   93
-------------------
*/
 

create table tb(姓名 varchar(10),课程 varchar(10),分数 int)
insert into tb values('张三' , '语文' , 74)
insert into tb values('张三' , '数学' , 83)
insert into tb values('张三' , '物理' , 93)
insert into tb values('李四' , '语文' , 74)
insert into tb values('李四' , '数学' , 84)
insert into tb values('李四' , '物理' , 94)
go 

--SQL SERVER 2000 静态SQL,指课程只有语文、数学、物理这三门课程。(以下同)
select 姓名 as 姓名 ,
  
max(case 课程 when '语文' then 分数 else 0 end语文,
  
max(case 课程 when '数学' then 分数 else 0 end数学,
  
max(case 课程 when '物理' then 分数 else 0 end物理
from tb
group 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)<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. FROM:
   对FROM子句中的前两个表执行笛卡尔积,生成虚拟表VT1。
2. ON:
   对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2。
3. OUTER(JOIN):
   如果指定了OUTER JOIN,保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。
   如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
4. 对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。

   注:having不能单独使用,having子句是对分组后的记录的筛选,所以有having必须要有group by
8. SELECT:
   处理SELECT列表,产生VT8。
9. DISTINCT:
   将重复的行从VT8中移除,产生VT9。
10. 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 雇员)            ------第二页

 

2. 准备数据

SET NOCOUNT ON;
USE tempdb;
GO

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 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);

  执行结果:

3. 查询语句

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

  执行结果:

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

 

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

    执行结果VT1:

 

2. 应用ON 筛选器,只有<join_condition>为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:

  

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

  

4. 应用WHERE筛选器,只有符合<where_condition>的行才会成为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:

  

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

 

 

  Groups

 

 

  Raw

 

 

  C.customerid

 

 

 

 

  FISSA

 

 

  FRNDO

 

 

  KRLOS

 

 

  VT5由两部分组成:Group Section和Raw Section。

  如果在查询中指定了GROUP BY子句,则后面的所有步骤(如:HAVING、SELECT)只能指定可以为成组得到的标量值的表达式。也就是说,表达式的结果是GROUP BY列表中的列/表达式(如:C.customer)或聚合函数(如:COUNT(O.orderid))。该限制是因为最终的结果集中最多只为每一个组 包含一行。

  这一阶段认为两个NULL是相等的。所有的NULL值会被分配到一组。

  如果指定GROUP BY ALL,则在WHERE筛选中被移除的组将被添加到VT5中,且原始部分为空集合。在后面的步骤中,对该组应用COUNT聚合函数的结果将为0,应用其他聚合函数的结果为NULL。最好不要使用GROUP BY ALL。

  

6. 使用CUBE或ROLLUP选项,将创建超组并把它添加到上一步返回的虚拟表中,生成VT6。

  

7. 应用HAVING 筛选器 ,只有符合<having_condition>的组才会成为VT7的一部分。HAVING是唯一的应用到已分组数据的筛选器。

HAVING COUNT(O.orderid) < 3

  在这里使用了COUNT(O.orderid),而不是COUNT(*),所以外部行因为O.orderid为NULL,于是不计入COUNT中。如FISSA这组的COUNT(O.orderid)为0.

  红色部分为被HAVING筛选掉的分组。

 

 

  Groups

 

 

  Raw

 

 

  C.customerid

 

 

 

 

  FISSA

 

 

  FRNDO

 

 

KRLOS

 

 

  

8. 处理SELECT列表,为不是基列的表达式应用别名,使其在结果表中有一个名称。在SELECT列表中创建的别名不能再前面的步骤中使用,甚至不能再SELECT列表中使用,只能在ORDER BY中使用。

SELECT C.customerid, COUNT(O.orderid) AS numorders

  生成VT8:

  逻辑上,应当假设所有操作同时发生。

  

9. 应用DISTINCT子句,如果查询中指定了DISTINCT子句,将从上一步返回的虚拟表中移除重复行,并生成虚拟表VT9。使用GROUP BY,再使用DISTINCT是多余的。

  

10. 应用ORDER BY子句,按照ORDER BY子句中的列列表排序上一步返回的行,返回游标VC10。只有这一步可以使用SELECT别名。如果指定了DISTINCT,ORDER BY子句中的表达式只能访问上一步返回的虚拟表,只能按已经SELECT的列排序。

  ANSI SQL 1999中增强了ORDER BY的支持,允许访问SELECT阶段的输入虚拟表和输出虚拟表。就是说如果未指定DISTINCT,可以在ORDER BY子句中指定任何可以在SELECT子句中使用的表达式,可以按最后结果集中不存在的表达式排序。

ORDER BY numorders;

  也可以在ORDER BY子句中指定SELECT列表中结果列的序号:

ORDER BY 2, 1;

  但是尽量不要这样去做,因为可能改变了SELECT列表却忘记了修改ORDER BY列表,而且当SELECT列表很长时,查序号不是一个好方法。

  因为这一步不是返回表,而是返回游标,使用了ORDER BY子句的查询不能用作表表达式。表表达式包括:视图、内联表值函数、子查询、派生表和共用表表达式(CTE)。

  不要为表中的行假定顺序,除非确实需要有序行,否则不要指定ORDER BY子句。排序是需要成本的,SQL Server需要执行有序索引扫描或使用排序运算符。

  ORDER BY这一步认为两个NULL是相等的,所有的NULL会被排列在一起,ANSI并没有规定NULL比已知值高还是低,而是把这个问题留给了具体实现,在T-SQL中NULL排位比已知值低。

ORDER BY numorders

  返回的游标VC10:

 

11. 应用TOP选项,从游标的最前面选择指定的行数,生成表VT11并返回给调用者。在SQLServer 2000中,TOP的输入必须为常量,而在2005中可以是任何独立的表达式。

  如果没有ORDER BY子句或WITH TIES选项,返回的行正好是物理上最先访问的行,可能会产生不同的结果。

  只有指定了TOP选项,才可以在表表达式中使用带有ORDER BY子句的查询:

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

 

原文:http://tech.ddvip.com/2009-04/1239449159114603.html

了解SQL嵌套语句执行顺序执行过程

SQL嵌套语句执行顺序

首先我们要了解一下SQL语句的执行过程。SELECT字段FROM表名WHERE条件表达式那它们是按什么顺序执行呢?
分析器会先看语句的第一个词,当它发现第一个词是SELECT关键字的时候,它会跳到FROM关键字,然后通过FROM关键字找到表名并把表装入内存。接 着是找WHERE关键字,如果找不到则返回到SELECT找字段解析,如果找到WHERE,则分析其中的条件,完成后再回到SELECT分析字段。最后形 成一张我们要的虚表。其它的先不说了,只说WHERE。WHERE关键字后面的是条件表达式。
如果学过C语言等编程语言就会知道,条件表达式计算完成后,会有一个返回值,即非0或0,非0即为真(true),0即为假(false)。同理WHERE后面的条件也有一个返回值,真或假,来确定接下来执不执行SELECT。

例:SELECT*FROM STUDENT WHERE SNO='1';分析器先找到关键字SELECT,然后跳到FROM关键字将STUDENT表导入内存,并通过指针p1找到第一条记录,接着找到 WHERE关键字计算它的条件表达式,如果为真那么把这条记录装到一个虚表当中,p1再指向下一条记录。如果为假那么p1直接指向下一条记录,而不进行其 它操作。

一直检索完整个表,关把虚表返回给用户。再说EXISTS谓词,EXISTS谓词也是条件表达式的一部分。当然它也有一个返回值(true或 false)。例:SELECT Sname FROM Student WHERE EXISTS(SELECT*FROM SC WHERE SC.Sno=Student.Sno AND SC.Cno='1');这是一个SQL语句的嵌套使用,但和上面说的SQL语句的执行过程也是相同的。嵌套的意思也就是说当分析主SQL语句(外面的那 个SELECT,我们权且先这么叫它)到WHERE关键字的时候,又进入了另一个SQL语句中。那么也就是说,分析器先找到表Student并装入内存, 一个指针(例如p1)指向Student表中的第一条记录。然后进入WHERE里分析里面的SQL语句,再把SC表装入内存,另一个指针(例如p2)指向 SC表中的第一条记录,分析WHERE后面的条件表达式,依次进行分析,最后分析出一个虚表2,也就变成SELECT Sname FROM Student WHERE EXISTS虚表2如果虚表为空表,EXISTS虚表2也就为false,不返回到SELECT,而p1指向下一条记录。

如果虚表2不为空也就是有记录,那么EXISTS虚表2为true同,返回到SELECT并把p1指向的记录添加到主SQL语句的虚表1当中。(这也是为 什么嵌套的SQL语句SELECT后面为一般为*的原因,因为它EXISTS返回的只是真或假,字段的名没有意义,用*就行,当然用别的也不会错。)注 意,这里虽然嵌套的SQL语句分析完了,但主SQL语句只执行了一遍,也就是说p1指向Student的第一条记录,p1还要再指向Student表的下 一条记录并分析,这样又进入了嵌套中的SQL语句,同上面说的一样分析。当p1也到了Student表的结尾,整个SQL语句结束。返回虚表1Sname 这一列。嵌套就像:for(int i=0,i n, i)for(int j=0,j n, j

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值