第1章 逻辑查询处理
Logical Query Processing
观察各个领域中真正的专家,你会发现,他们通常都有着非常扎实的基础知识。不管怎样,各行各业都需要处理各种问题。这些问题的所有解决方案(也许非常复杂)都需要综合运用多种关键技术。要想精通某一领域,你需要有坚实的基础,并基于此形成自己的知识体系。通过努力完善自身的技术,把基础打牢,这样你就可以解决任何问题。
本书讲述的是Transact-SQL(T-SQL)查询,你将从中学到大量的关键技术并用它们解决各种问题。本书先介绍逻辑查询处理的基础知识,我想不出有别的更好的开篇。我认为这一章是本书最重要的一部分,这不仅因为它涵盖了查询处理的核心,还因为SQL编程与其他类型的编程在概念上有很大不同。
SQL的Microsoft SQL Server方言——Transact-SQL――遵循ANSI标准。Microsoft SQL Server 2000在Entry SQL级别上符合ANSI SQL:1992标准,而Microsoft SQL Server 2005还实现了ANSI SQL:1999和ANSI SQL:2003中的一些重要特性。
在本书中,我会交替使用SQL和T-SQL这两个术语。当讨论源自ANSI SQL且与大多数方言有关的语言时,我通常使用术语SQL;当结合SQL Server具体实现来讨论语言的特性时,我将使用术语T-SQL。它的正式名称是Transact-SQL,但一般都被称为T-SQL。大部分程序员(包括我)都觉得把它称为T-SQL更方便,因此我决定在书中使用术语T-SQL。
SQL发音的起源
很多讲英语的数据库专业人员把SQL发成sequel的音,但正确的发音应该是S-Q-L("ess kyoo ell")。大家可以猜测一下导致这种错误发音的原因,我认为这其中既有历史上的原因也有语言学方面的原因。
在历史原因方面,IBM在20世纪70年代开发了一种叫做SEQUEL的语言,它是Structured English QUEry Language的首字母缩写,设计这种语言是为了操纵存储在数据库系统System R中的数据,System R基于Edgar F.Codd博士提出的关系数据库管理系统(RDBMS)模型。后来,由于商标之争,首字母缩写SEQUEL被简化成SQL。ANSI在1986年选择SQL作为一项标准,ISO则在1987做了同样的举措。ANSI宣布该语言的正式发音是“ess kyoo ell”,但它好像并没有被广泛接受。
在语言学方面,sequel的发音更为流畅,对讲英语的人来说更是如此。我自己也因为这个原因而使用这个发音。
有时你可以通过检查人们的书写来猜测它使用的是哪种发音,写成“an SQL Server”的人可能使用正确的发音,而写成“a SQL Server”的人则可能使用错误的发音。
| 更多信息 http://www.wikimirror.com/SQL记载了关于SQL及其发音的历史,非常有趣,强烈建议你阅读。Wikimirror和本章提到的SQL历史均基于来自Wikipedia(免费的百科全书)中的一篇文章。 |
SQL编程有许多独特的方面,如:面向集合的编程思想,查询元素的逻辑处理次序,三值逻辑(three-valued logic)等。如果不掌握这些知识就开始用SQL编程,你将得到冗余的代码,不仅性能低下而且难以维护。本章的目的是帮助你按设计者对SQL的构想来理解它。你需要为本章其余的部分打下坚实的基础。在适当的地方,我会明确指出哪些元素是T-SQL特有的。
在本书中,我将介绍各种复杂问题和高级技巧。正如我所提到的,本章只介绍查询的基本原理。整本书中涉及性能的笔墨颇多,但这一章只涉及查询处理的逻辑方面。我建议你在阅读本章时尽量不要考虑性能问题,本书后面的部分包含大量性能方面的内容,我在本章中描述的某些逻辑处理步骤可能看起来有些低效。但在实践中要记住:查询的实际物理处理可能与逻辑处理有很大不同。
在SQL Server中负责生成实际工作计划(执行计划)的组件是查询优化器,以何种顺序访问表、使用哪种访问方法和哪个索引、应用哪种联接算法等都是由优化器来决定的。优化器会生成多个有效的执行计划,并选择其中成本最低的执行计划。逻辑查询处理中的各个阶段都有着特定的顺序。另一方面,优化器却经常在它生成的物理执行计划中走捷径,当然,只有在保证结果集是正确的情况下优化器才会走捷径,换句话说,走捷径所得到的结果集必须与遵循逻辑处理的各个阶段所得到的结果集相同。例如,为了使用索引,优化器可能会决定使用一个比逻辑处理所规定的筛选器快得多的筛选器。
鉴于上述原因,明确地区分查询的逻辑处理和物理处理是非常有必要的。
闲言少叙,下面我们开始深入探讨查询的逻辑处理。
逻辑查询处理中的各个阶段
本节介绍逻辑查询处理所涉及的各个阶段。我先简要描述一下每个阶段,然后在后面的几节中对它们进行更为详细的介绍,并把它们应用到一个示例查询。当回忆各个阶段的含义和顺序时,你可以将本节作为一个快速参考。
代码清单1-1列出了查询的一般形式,并根据各个子句被逻辑处理的顺序附以步骤序号。
代码清单1-1 逻辑查询处理的步骤序号
(8) SELECT (9) DISTINCT (11) |
SQL不同于与其他编程语言的最明显特征是处理代码的顺序。在大多数编程语言中,代码按编码顺序被处理,但在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但几乎总是在最后被处理。
每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只有最后一步生成的表才会返回给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。下边是对应用于SQL Server 2000和SQL Server 2005的各个逻辑步骤的简单描述。在本章的后面,我将单独讨论SQL Server 2005中新增的步骤。
逻辑查询处理阶段简介
如果你觉得现在对这些步骤的描述没有多大意义,不用太着急,它们只是一个概述。示例查询后面有一节将详细描述这些步骤。
1. FROM: 对FROM子句中的前两个表执行笛卡尔乘积(Cartesian product)(交叉联接),生成虚拟表VT1。
2. ON: 对VT1应用ON筛选器。只有那些使
3. OUTER(JOIN): 如果指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表(preserved table)中未找到匹配的行将作为外部行添加到VT2,生成T3。如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
4. WHERE: 对VT3应用WHERE筛选器。只有使
5. GROUP BY: 按GROUP BY子句中的列列表对VT4中的行分组,生成VT5。
6. CUBE|ROLLUP: 把超组(Supergroups)插入VT5,生成VT6。
7. HAVING: 对VT6应用HAVING筛选器。只有使
8. SELECT: 处理SELECT列表,产生VT8。
9、 DISTINCT: 将重复的行从VT8中移除,产生VT9。
10. ORDER BY: 将VT9中的行按ORDER BY子句中的列列表排序,生成一个游标(VC10)。
11. TOP: 从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者。
| 回书目 上一节 下一节 |