前言

前言

SQL 是数据库世界的语言。如果你从事与关系数据库相关的开发工作或者数据报表工作,那么把数据存入数据库并将其再次读取出来的能力最终取决于你所具备的 SQL 知识。然而,许多数据库从业人员只是粗浅地使用 SQL,并不了解其强大的数据处理能力。本书致力于改变这种状况,告诉你 SQL 真正能帮你做些什么。

你手里的这本书像一本菜谱,它包含了一系列常见的 SQL 问题及其解决方案。我希望它能对你的日常工作有所帮助。我按照主题组织章节,当你遇到一个不好解决的 SQL 问题时,请查找最相近的章节。浏览一下各节的标题,你很可能会找到解决思路,或至少得到一些启发。

本书汇集了 150 多个实例,篇幅达 500 多页。然而,这样的篇幅仅仅展示了 SQL 实际能力的一个侧面。我们为日常的编程问题找到了不同的 SQL 解决方案,但其数量远未及问题的数量。本书并不打算集齐全部 SQL 编程问题。事实上,那样做徒劳无功。相反,你能在书中找到许多常见问题的解决方案,从中学到的技术将有助于拓宽你的思路并用于解决新问题。

 出版社和我本人持续关注那些新的、有价值的 SQL 解决方案。如果你对某个 SQL 问题有聪明的解决办法,不妨分享出来,或许我会将其加入本书的下一版。可以在“联系我们”一节找到我们的联系方式。

为何写作本书

查询、查询,还是查询。我最初的目标是写一本讲解 SQL 查询技术的书,而不是现在的这本涵盖范围如此广泛的 SQL 实例集。一开始,我专注于解释各种 SQL 查询,从相对简单的语句起步,逐步过渡到比较复杂的部分;我盼望你能掌握其中的技巧,进而运用它们解决工作中遇到的实际问题。我希望能把多年职业生涯中积累的许多 SQL 编程技巧传授给你,希望你能从中学到知识,得到启发,最终找到更好的解决方案。我相信在这个过程中你我彼此都会受益良多。从数据库里读取数据看起来简单至极,然而在 IT 的世界里尽可能高效地实现数据检索却是至关重要的事情。关于高效检索的技术应该被广泛地分享和传播,这样能提升所有人的效率,使大家互帮互助。

想想为数学做出杰出贡献的格奥尔格 • 康托尔,他最先意识到把一组元素作为整体来研究有着重大意义(即研究集合本身,而不是研究各个构成要素)。最初,康托尔的工作并不为数学界所接受。然而现在,大家不仅接受了它,甚至认为集合论是数学的基础!更重要的是,集合论能有今天的面貌,并非仅仅得益于康托尔一个人的研究工作;通过向同行分享研究成果,其他诸如 Ernst Zermelo、Gottlob Frege、Abraham Fraenkel、Thoralf Skolem、Kurt Gödel 和 John von Neumann 等数学家进一步发展和改进了集合论。分享不仅让大家更好地理解了康托尔的研究工作,也造就了更完善的集合论。

本书的目标

本书的终极目标是带领你看看 SQL 除了解决典型的问题之外,还能做些什么。在这些年里,SQL 获得了长足的发展。过去我们通常使用 C 或者 Java 等过程化编程语言才能解决的问题,现在已经可以直接用 SQL 解决了,而许多开发人员却对此一无所知。本书将带你学习这些方面的 SQL 技术。

不过,为了避免你误解上述文字,我要声明,我真心赞同一句老话:“如果东西没坏,就别修理。”举例而言,假定有一个具体的业务问题要解决,你先用 SQL 从数据库里取出了原始数据,然后用其他编程语言写程序,并实现了一些复杂的业务逻辑处理。如果你的代码工作正常并且运行起来性能也不错,那么,保持现状就很好。我不会怂恿你去实现一个“纯 SQL 解决方案”,我只是希望你能意识到,今天的 SQL 不同于 1995 年的 SQL。今天的 SQL 实际上能做更多的事情。

本书的读者对象

本书的独特之处在于,它面向广泛的读者群,且最终呈现出来的内容保持了高品质。我在书中同时提供了复杂的和简单的实例,如果某个问题没有通用的解决办法,我会针对不同的数据库产品提供多种方案供你选择。因此本书的目标读者群确实是广泛的。

  • SQL 初学者:或许你买了一本教材,想开始学习 SQL;或许你刚开始上第一个学期的数据库必修课,想通过研究实例巩固课堂所学。你可能看到过有人用区区一条查询语句就神奇地把行形式的数据转换成了列形式,或者把某个长字符串拆解成了一组结果集。本书收录的众多实例将解释上述这些神奇查询背后的技术。
  • 非 SQL 程序员:或许你有其他语言的编程经验,而当前的工作急需你掌握别的同事留下的一些复杂的 SQL。本书列出的实例(尤其是后面几章)会把复杂的查询一一分解开来,帮你循序渐进地理解复杂的代码。
  • SQL 开发人员:对于中级 SQL 开发人员,本书是你梦寐以求的进阶灵丹妙药。(好吧,这话说得太大了。请原谅一位作者对作品的自信。)如果你从很久以前就开始用 SQL 编程了,并且想开始学习窗口函数,那么本书尤其适合你。举例来说,你不再需要把中间计算结果存入临时表;有了窗口函数,你只要一个 SQL 查询就能得出结果。我要再次声明,我并不是在勉强你接受我的观点。但是,如果你还没有适时跟进 SQL 语言最新的变化,请借助本书更新你的技能。
  • SQL 专家:毫无疑问,你早已经掌握了书中的技巧,甚至已经能够加以变化、灵活运用。那么,本书对你是否还有帮助呢?也许你精通 SQL Server,想要了解 Oracle;也许你只用过 MySQL,又想知道同样的技术在 PostgreSQL 上是如何应用的。本书涉及多个不同的关系数据库管理系统,分别展示了针对不同产品的实例。这是你拓宽知识领域的好机会。

如何使用本书

请一定认真通读前言部分。它包含了一些必要的背景知识及其他信息,这为后续的内容做了适当的铺垫。“平台和版本”一节会告诉你本书涉及哪些关系数据库管理系统。尤其要看一下“本书中用到的表”一节,这样你会熟悉后续章节里反复出现的数据表样例。你也会在“本书使用的约定”一节中看到一些重要的代码和字体风格约定。上述内容都是前言的组成部分。

注意,这是一本 SQL 实例集,它用一系列的代码实例来帮助你解决你可能会遇到的相似或者相同的问题。请不要试图通过本书学习 SQL 语法和基本知识,至少它不适合那些对 SQL 一无所知的读者。本书适合作为补充材料,而不能替代通常的 SQL 教科书。除此之外,下述要点能帮你更好地利用本书。

  • 本书中用到了一些数据库厂商提供的函数。如果你不了解这些函数,不妨参考 Jonathan Gennick 的著作 SQL Pocket Guide,该书详细解释了这些函数。
  • 如果你不曾使用过窗口函数,或者不熟悉 GROUP BY 查询,请先阅读附录 A。该附录讲解了 SQL 的分组概念和做法,它也展示了窗口函数的工作原理。窗口函数的引入是 SQL 最为重要的进展之一。
  • 请尊重常识!务必了解,本书不可能涵盖你在工作中可能遇到的全部问题。你要做的是把本书提供的实例作为模板或者指南,灵活运用必要的技术来解决你遇到的问题。你可能会这样说:“太棒了!这个例子适用于这种特定的数据集,但我遇到的问题与此不同,以至于无法照搬。”在这种情况下,你应该试着找到两者之间的共性。把书中的查询语句拆解开来,先提取出最基本的形式,然后根据需要逐步增加难度。任何查询都从 SELECT...FROM... 这种基本形式起步。如果你在它的基础上逐步地添砖加瓦,每次增加一个新的查询项、函数或连接查询,你就不仅能充分理解每个动作会如何影响最终的结果集,也能了解书中的实例与你的实际需求之间有何种差异。最后,你就能修改这些实例,使其适用于你自己的数据集。
  • 测试、测试,再测试。本书中反复出现的 EMP 表只有 14 行,毫无疑问,你在工作中遇到的任何一张表都可能比它大。因此,我建议用你自己的数据来测试书中的查询,至少要保证它们工作正常。我没有办法了解你的表长什么样,有哪些索引,和其他表存在何种关联。因此,除非你全面地理解了书中的查询技巧以及把它们应用到你的数据里会有什么样的结果,否则请不要盲目地将它们应用于生产环境的代码。
  • 大胆尝试,勇于创新。我鼓励你大胆尝试本书未曾提及的那些技巧和做法。虽然我在书中刻意使用了不同数据库的许多函数,但通常而言,还有别的函数也同样适用于解决某个问题。因此,请大胆改写书中提供的代码,演化出你自己的版本。
  • 新东西不一定更好。如果你的代码里没有用到新近引入的 SQL 语言特性,并不意味着引入之后它会变得更有效率。在很多情况下,传统的做法可能和新方案一样好,甚至更好。请记住这一点,尤其是在阅读附录 B 的时候。读过本书以后,请不要认为你必须更新或者修改所有的旧代码。你只需要认识到,SQL 相比 20 年前添加了许多新的、非常好的功能,而它们值得你花时间去学习。
  • 不要害怕复杂的查询。如果你发现某个查询看起来太复杂,以至于暂时无法理解,不要害怕。当讲解一个问题时,我已经不遗余力地分解每一个查询,从最简单的形式开始逐级变化,直至呈现出完整的解法,我甚至列出了每个中间步骤的执行结果。你可能没办法立刻纵观全局,但只要跟着我的思路走下去,不仅能够理解查询语句是如何被构造出来的,也能看到中间的每一步会得到什么样的结果。最终,你会发现那些复杂的查询并不难理解。
  • 在必要的时候进行防御式编程。为了让本书中出现的查询尽量简洁易懂,我去除了代码里的许多防御性措施。以一个计算员工薪酬总和的查询为例。一种可能出现的情况是,表示薪酬的字段被定义成了 VARCHAR 类型,以至于存入数据库的可能是混合了数字和字符串的数据。我在书中给出的代码并没有提防这种情况(因此,SUM 函数因无法处理字符数据而导致执行失败)。如果你遇到了这样的数据(更准确地说是“这样的问题”),就需要通过额外的代码做一些防范处理,或者把不规范的数据整理好,因为本书中给出的查询并没有考虑这种数字和字符混合出现的情况。我的观点是,略去这类琐碎的细节有助于你聚焦正题,专注理解查询技术。
  • 反复练习最重要。掌握查询的最佳办法是亲自动手编程。阅读代码自然大有裨益,但动手练习是更好的做法。你当然要先读懂那些查询并了解其工作原理,但最终只有通过动手实践才能自己写出查询。

注意,本书中的很多例子都是人为设计的。然而,问题本身都来自真实世界,并非人为臆造。我只不过围绕着一小组包含了雇员数据这样的表来构造实例。我尽力帮你先熟悉示例数据,这样你就能把注意力放在每一个实例背后的技术细节上。面对某个具体问题时,你可能会说:“我不需要针对雇员数据做这些查询。”这时请忽略示例数据,聚焦于我为你演示的那些技术。技术是通用的。我和我的同事天天都在用同样的技术解决不同的问题。我们相信你也是这样。

本书不会涉及的内容

由于时间和篇幅限制,一本书无法囊括你可能实际遇到的所有 SQL 问题及其解决办法。以下是本书不会涉及的内容。

  • 数据定义。本书不会涉及诸如创建索引、添加约束、加载数据等 SQL 操作,这一类操作的语法多数会因数据库的不同而呈现出较大差异,因此你最好多参考官方手册。另外,这类任务的难度还没有达到那种需要专门买一本书来寻求解决方案的程度。尽管如此,第 4 章还是提供了一些涉及数据的插入、更新和删除等常见问题的实例。
  • XML。我一向认为,与 XML 相关的例子不应该出现在 SQL 书里。把 XML 文档存入关系数据库正变得越来越常见,以至于许多关系数据库管理系统都提供了专有的扩展和工具帮助大家获取和处理 XML 数据。处理 XML 通常需要一些过程化的程序代码,因此 不在本书讨论范围之内。XQUERY 等技术完全独立于 SQL,应该会有专门讲解这一类技术的书。
  • SQL 的面向对象扩展。除非出现更适合处理对象的语言,否则我不赞成在关系数据库里使用面向对象特性和设计。当前一些数据库实现了部分面向对象特性,不过它们更适用于过程化程序设计,而非 SQL 固有的面向集合的问题解决方式。
  • 理论层面的争论。你不会在本书中看到诸如 SQL 是不是关系型编程语言,或者 Null 是否应该存在等这一类观点。我把注意力集中在来自真实世界的 SQL 实例上,理论层面的讨论不见于本书。要解决一个问题,你必须对现有的工具善加利用。你只能拥抱现有的一切,而不应该对那些可望不可及的东西念念不忘。

     如果你希望学习更多理论知识,Chris Date 的“关系数据库论文集”系列里的任何一本书都会是一个非常好的起点。你也可以去读他的著作《深度探索关系数据库》。

  • 数据库优劣之争。本书提供的实例兼顾 5 种关系数据库管理系统。你自然想知道哪种数据库提供的方案最好或最快。每一家数据库厂商都能给出足够多的资料来证明自己的产品才是最好的,我不想在这里论及此事。

  • 数据库标准之争。许多书都有意回避不同数据库厂商提供的专有函数,本书却热情拥抱这些专有函数。我不会仅仅出于对可移植性的考虑去写一些复杂低效的 SQL 代码。我从来没有见过哪一家公司明令禁止使用专有的扩展。你付钱买了这些特性,为什么不善用它们?

    数据库厂商的专有扩展之所以存在,自有其原因。相较于标准 SQL,专有扩展往往能提供更高的执行效率和更强的代码可读性。如果你喜欢写完全符合 ANSI 标准的代码,那也很好。正如我之前提到的,我并不是要你去将现有代码改个底朝天。如果你的代码严格符合 ANSI 标准并且工作得很好,那也很棒。归根结底,我们都要工作,都要支付账单,并且都想早点下班回家以享受每天的剩余时光。因此,我并不是在暗示纯标准化的做法有问题。让代码跑起来才是最重要的事。但是,我需要声明,如果你在寻找纯标准化的解决方案,那就不应该阅读本书。

  • 遗留系统之争。本书提供的实例用到了写作本书时已经可用的数据库新特性。如果你还在使用某些旧版本的关系数据库管理系统,我提供的解决方案有许多可能都无法适用。技术不会停滞不前,你也不应该墨守成规。如果你需要找到针对旧版关系数据库管理系统的解决方案,可以翻翻多年前的 SQL 书,它们已经提供了足够多的例子。

本书的结构

本书包括 14 章和两个附录。

  • 第 1 章介绍非常基本的查询语句。示例包括如何使用 WHERE 子句筛选结果集,为结果集里的列取别名,通过内嵌视图实现别名列引用,使用简单的条件逻辑,限制单个查询返回的记录行数,随机返回记录行,以及检索 Null 值。大多数示例都非常简单,但部分示例会再次出现在后续较为复杂的实例里。如果你是 SQL 新手或者对这些用法不太熟悉,那么应该好好读一读这一章的内容。
  • 第 2 章提供了一些查询结果排序的实例。这一章介绍了 ORDER BY 子句,并将其用于查询结果排序。示例的难度逐步增加,从简单的单列排序到按照子字符串排序,再到按照条件表达式排序。
  • 第 3 章通过一些实例来演示如何合并多个表的数据。如果你是 SQL 新手或不熟悉连接查询,我强烈建议你在阅读第 5 章及后续章节之前先读一读这一章的内容。连接查询几乎就是 SQL 的全部内容,要学会 SQL,你必须理解连接查询。这一章的示例包括执行内连接和外连接,识别笛卡儿积,执行基本的集合操作(差集、并集、交集),以及在连接查询中使用聚合函数。
  • 第 4 章分别提供了一些关于插入、更新和删除数据的实例。它们中的大多数都很直截了当(甚至可能让你觉得乏味)。然而,有一些操作可能对你非常有用,比如把一个表的若干行插入另一个表,更新数据时使用关联子查询,理解 Null 值的作用,以及掌握多表插入和 MERGE 命令等特性的用法。
  • 第 5 章通过一些实例讲解如何获取数据库的元数据信息。找出一个数据库的索引、约束和表往往非常有用。这些简单的例子帮助你获取关于数据库模式的信息。除此之外,这一章也包括一些动态 SQL 示例,例如用 SQL 生成新的 SQL。
  • 第 6 章介绍了一些处理字符串的实例。SQL 的字符串解析能力并不出众,但基于数据库的大量专有函数,再加上一点点创意(通常要用到笛卡儿积),你就能完成不少工作。其中一些更加有趣的例子包括计算一个字符在某个字符串里出现过多少次,基于表的若干行生成列表,把列表和字符串转换成行数据,以及从一个字母和数字混合的字符串里提取数值和字符。
  • 第 7 章给出了一些常见的数字运算实例。这些例子极具通用性,并且能让你体会到窗口函数在解决涉及动态计算和聚合的问题时有多方便。这一章的示例包括计算累加值;计算平均数、中位数和众数;计算百分比;在聚合运算中排除 Null 值。
  • 第 8 章是涉及日期处理的两章中的第 1 章。对于日常任务来说,处理简单的日期运算十分重要。这一章给出的示例包括计算两个日期之间有多少个工作日,以不同的时间单位(天、月、年等)算出两个日期的差值,以及统计一年中有多少个星期一。
  • 第 9 章是涉及日期处理的第 2 章。你会发现日常工作中最为常见的日期操作实例,包括返回一年包含的所有天,计算闰年,算出一个月的第一天和最后一天,生成日历,以及填补一个日期范围里缺失的日期。
  • 第 10 章通过一些实例演示如何识别指定范围内的值,以及如何创建一系列的值。示例包括自动生成一系列行数据,填补一个数值范围里缺失的值,查找一个范围的开始值和结束值,以及查找连续的值。
  • 第 11 章中的例子有时被开发人员忽视,但对于日常开发工作来说至关重要。这些例子绝不比其他例子更难,但我却见到许多开发人员用非常低效的做法来解决同样的问题。这一章的示例包括查找“骑士值”,为结果集分页,跳过表里的某些行,逆序查找,检索靠前的 n 行,以及为查询结果排序。
  • 第 12 章提供了一些数据仓储和复杂报表生成领域常见的查询。我最初的愿望就是把这一章的内容作为本书的主体部分。这一章的示例包括行列互换(交叉报表),创建数据分组,创建直方图,计算出简单而完整的小计值,在一个动态的行数据窗口之上执行聚合计算,以及基于给定的时间单位做行数据分组。
  • 第 13 章介绍了一些与层次化有关的实例。无论采用何种建模方式,你总会在某个时刻需要做数据格式化工作,比如以树形结构或者父子关系形式展现出来。这一章提供的例子能够帮你完成这些任务。利用传统的 SQL 创建树形结构的数据集并不容易,所以数据库厂商提供的专有函数在这一章里显得尤其有用。示例包括呈现数据的父子关系,从根节点到叶子节点逐层遍历,以及构造一个层次结构。
  • 第 14 章是各种实例的大杂烩,它们很难被归入某个问题领域,却既有趣又有用。这一章不同于其他各章之处在于,它只聚焦于数据库厂商提供的专有特性。每个实例只针对一种数据库而设,这是全书唯一这么做的一章。有两个原因促使我这么做:第一,我想让这一章的内容既有趣又带些许极客风格;第二,有些实例的存在就是为了突出某个数据库厂商的专有函数,因为在其他关系数据库管理系统里没有等价实现(示例包括 SQL Server 的 PIVOT/UNPIVOT 操作符和 Oracle 的 MODEL 子句)。在某些情况下,你能简单地改造一下这一章提供的解决方案,将其用于另一种数据库。
  • 附录 A 带你复习窗口函数的相关知识,并且详细讨论了 SQL 分组查询。你可能不熟悉窗口函数,附录 A 可以帮助你快速入门。此外,根据我的经验,GROUP BY 查询的使用一直令许多开发人员感到迷惑。附录 A 精确定义了何为 SQL 分组查询,并且给出了多种查询示例,以进一步解释该定义。接着,附录 A 讨论了 Null 值对分组、聚合以及分区的影响,最后讨论了窗口函数中更难理解却功能强大的 OVER 子句(即开窗子句)。
  • 附录 B 主要是向 David Rozenshtein 致敬,并把我在 SQL 开发方面的成就归功于他。Rozenshtein 的作品 The Essence of SQL 是我在课堂之外买的第一本 SQL 书。我当时买这本书,并非为了应付考试。正是这本书教会了我如何用 SQL 思考。时至今日,我仍把自己关于 SQL 工作原理的许多心得体会归功于这本书。与我读过的其他 SQL 书相比,它是如此与众不同,我为它能成为我的第一本 SQL 书而充满感激之情。我在附录 B 中重新审视了 The Essence of SQL 里出现过的一些查询语句,并给出了使用窗口函数实现的新解决方案。(在 The Essence of SQL 出版时,窗口函数尚未出现。)

平台和版本

SQL 产品日新月异。各个厂商不断地为各自的产品加入新特性和新功能。因此,我要先告诉你本书是为各个数据库产品的哪些版本而准备的。

  • DB2 v.8
  • Oracle Database 10g(除了少数实例,本书的解决方案也都适用于 Oracle8i 和 Oracle9i
  • PostgreSQL 8
  • SQL Server 2005
  • MySQL 5

本书中用到的表

本书中的大部分例子都会涉及两个表:EMP 表和 DEPT 表。EMP 表有 14 行数据,它非常简单,仅用到了数字、字符串和日期字段。DEPT 表有 4 行数据,它也很简单,只含有数字和字符串字段。这两个表在许多现存的数据库教科书里都曾出现过,大家也早已熟知在员工和部门之间所存在的多对一关系。

当我谈到示例表的话题时,我想说本书中的示例除了极少数的几个,几乎所有的示例都会用到这两个表。我不会像其他一些书那样,通过修改示例数据来构造一些你绝对不可能在真实世界里实现的解决方案。

说到解决方案,请允许我稍微提一下,只要状况允许,我都会尽可能地为本书涉及的 5 种关系数据库管理系统提供通用的解决方案。但经常无法做到这一点。尽管如此,在许多情况下,多种数据库可能共用一种解决方案。举例而言,因为相互支持对方的窗口函数,所以 Oracle 和 DB2 经常共用解决方案。如果解决方案共用或非常类似,那么在讨论部分也会一并提及。

EMP 表和 DEPT 表的数据分别如下所示。

select * from emp;

EMPNO ENAME   JOB        MGR HIREDATE     SAL COMM  DEPTNO
----- ------  --------- ---- ----------- ---- ---- -------
 7369 SMITH  CLERK      7902 17-DEC-1980  800           20
 7499 ALLEN  SALESMAN   7698 20-FEB-1981 1600  300      30
 7521 WARD   SALESMAN   7698 22-FEB-1981 1250  500      30
 7566 JONES  MANAGER    7839 02-APR-1981 2975           20
 7654 MARTIN SALESMAN   7698 28-SEP-1981 1250 1400      30
 7698 BLAKE  MANAGER    7839 01-MAY-1981 2850           30
 7782 CLARK  MANAGER    7839 09-JUN-1981 2450           10
 7788 SCOTT  ANALYST    7566 09-DEC-1982 3000           20
 7839 KING   PRESIDENT       17-NOV-1981 5000           10
 7844 TURNER SALESMAN   7698 08-SEP-1981 1500    0      30
 7876 ADAMS  CLERK      7788 12-JAN-1983 1100           20
 7900 JAMES  CLERK      7698 03-DEC-1981  950           30
 7902 FORD   ANALYST    7566 03-DEC-1981 3000           20
 7934 MILLER CLERK      7782 23-JAN-1982 1300           10

select * from dept;

DEPTNO DNAME          LOC
------ -------------- ---------
    10 ACCOUNTING     NEW YORK
    20 RESEARCH       DALLAS
    30 SALES          CHICAGO
    40 OPERATIONS     BOSTON

除此之外,本书还会用到 4 张数据透视表:T1T10T100T500。因为这些只是数据透视表,所以我认为不需要给它们取更容易懂的名字。关于表名,跟在字母 T 后面的数字表示该表有几行数据,每行有一个序号,从 1 开始。例如,T1 表和 T10 表的数据如下所示。

select id from t1;

        ID
----------
         1

select id from t10;
        ID
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

顺便说一下,一些数据库支持局部 SELECT 语句。举例来说,可以只有 SELECT 而没有 FROM 子句。我不喜欢这样的用法,因此我构造了 T1 这样只有一行数据的表并针对该表执行查询,而没有使用局部查询。

任何其他仅用于特定实例和章节的表,我会在书中的适当位置做出解释。

本书使用的约定

本书遵循了许多排版和代码编写约定。请花点时间熟悉它们,这有助于加深你对本书内容的理解。代码编写约定尤其重要,因为我不能在每一个实例中都重复强调一遍。因此,我把一些重要的约定都列在下面。

排版约定

本书遵循下列排版约定。

大写字母表示 SQL 关键字。

小写字母用于所有代码示例。诸如 C 和 Java 这样的编程语言都使用小写形式的关键字,我发现其可读性比大写形式更好。因此,本书中所有查询语句都使用小写形式。

等宽粗体用于在交互示例里表示用户输入的内容。

 这个图标表示小技巧、建议或者一般性提示。

 

 这个图标表示警告或注意事项。

代码编写约定

我习惯在 SQL 语句中全部用小写字母,不管是关键字还是用户指定的标识符。例如:

select empno, ename
  from emp;

你的习惯可能不同。例如,许多人喜欢把 SQL 关键字大写。你需要遵循你喜欢的或者项目要求的风格。

尽管代码示例用小写形式,但在正文里 SQL 关键字和标识符始终都是大写字母。我这样做是为了将它们和其他普通文本明确区分开来。例如,前述查询语句展示了一个针对 EMP 表的 SELECT 操作。

尽管本书涵盖了 5 个数据库厂商的产品,但是我还是决定用同样的格式呈现所有产品的输出结果。

EMPNO ENAME
----- ------
 7369 SMITH
 7499 ALLEN
…

许多实例在 FROM 子句里用到了内嵌视图或子查询。ANSI SQL 标准规定要给它们取别名。(只有 Oracle 不要求指定这一类别名。)因此,我在解决方案里经常用类似 xy 这样的别名来标识内嵌视图。

select job, sal
  from (select job, max(sal) sal
          from emp
        group by job) x;

注意最后紧挨着圆括号的字母 X。在这里,字母 X 变成了 FROM 子句里那个子查询返回的表的名字。列别名是一个有用的工具,能帮我们写出自注释的代码;相对而言,(本书中出现过的多数)内嵌视图的别名只是一种形式化的东西。通常我会为它们取一个简单的名字,诸如 XYZTMP1TMP2。在某些情况下,如果我觉得取一个更好的别名有助于增加可读性,我就会那样做。

你将会看到在每个实例的解决方案部分出现的 SQL 语句,其每一行都会被编号,例如:

1 select ename
2     from emp
3  where deptno = 10

这些数字并不是语法的一部分,我把它们包含进来只是为了方便在每个实例的讨论部分能使用序号来引用查询语句里的各个部分。

使用示例代码

本书的目的在于帮助你完成工作。一般来说,你可以在你的程序和文档中使用本书的代码。只要不是大规模地复制代码,你就不需要联系我们取得授权。举例而言,你写的一个程序用到了本书的几个代码片段,这是不需要授权的。但是,如果你把书中的示例代码刻录到 CD-ROM,并拿去出售和分发,则需要获得授权。在回答问题时引用本书以及本书的示例代码无须取得授权。但如果要在你的产品文档里收录本书中出现过的大量示例代码,则需要获得授权。

欢迎你在使用本书的示例代码时注明出处,但这不是强制要求。通常要注明书名、作者、出版社和 ISBN。例如:SQL Cookbook, by Anthony Molinaro. Copyright 2006 O'Reilly Media, Inc., 0-596-00976-3。

如果你认为你对示例代码的使用不在合理使用和上述无须授权的范围之内,那么请通过 permissions@oreilly.com 联系我们。

联系我们

请把对本书的评价和问题发给出版社。

美国:

  O'Reilly Media, Inc.

  1005 Gravenstein Highway North

  Sebastopol, CA 95472

中国:

  北京市西城区西直门南大街 2 号成铭大厦 C 座 807 室(100035)

  奥莱利技术咨询(北京)有限公司

O'Reilly 的每一本书都有专属网页,你可以在那儿找到本书的相关信息,包括勘误表、示例代码以及其他信息。本书的网站地址是:http://shop.oreilly.com/product/9780596009762.do

对于本书的评论和技术性问题,请发送电子邮件到:bookquestions@oreilly.com

要了解更多 O'Reilly 图书、培训课程、会议和新闻的信息,请访问网站:

  http://www.oreilly.com

我们在 Facebook 的地址是 http://facebook.com/oreilly

请关注我们的 Twitter 动态:http://twitter.com/oreillymedia

我们的 YouTube 视频地址是 http://www.youtube.com/oreillymedia

Safari® Books Online

Safari Books Online(http://www.safaribooksonline.com)是应运而生的数字图书馆。它同时以图书和视频的形式出版世界顶级技术和商务作家的专业作品。技术专家、软件开发人员、Web 设计师、商务人士和创意专家等,在开展调研、解决问题、学习和认证培训时,都将 Safari Books Online 视作获取资料的首选渠道。

对于组织团体、政府机构和个人,Safari Books Online 提供各种产品组合和灵活的定价策略。用户可通过一个功能完备的数据库检索系统访问 O'Reilly Media、Prentice Hall Professional、Addison-Wesley Professional、Microsoft Press、Sams、Que、Peachpit Press、Focal Press、Cisco Press、John Wiley & Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、Adobe Press、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones & Bartlett、Course Technology 以及其他几十家出版社的上千种图书、培训视频和正式出版之前的书稿。要了解 Safari Books Online 的更多信息,我们网上见。

致谢

如果没有许多人的帮助和支持,就不会有本书。我想感谢我的妈妈 Connie,我把本书献给她。没有她的努力工作和奉献就不会有今天的我。妈妈,感谢您为我做的一切,我很感激您为我和哥哥所做的一切,成为您的儿子是我的荣幸。

我要感谢我的哥哥 Joe。每次我暂停写作,从巴尔的摩回到家休息时,你让我知道与家人相处的时光是多么重要,让我意识到我应该尽快完成写作,以回到生活中更重要的事情中来。你是一个善良的人,我尊敬你。我非常为你自豪,为你是我的哥哥而感到自豪。

我要感谢我出色的未婚妻 Georgia。没有你的支持,我不可能完成这本书。你和我一起见证了本书的创作过程。我知道这对你来说很困难,对我也一样。我白天忙于工作,晚上忙于写作,但你包容了这一切。我永远感激你的理解和支持。谢谢你,我爱你。

我要感谢我未来的岳母 Kiki 和岳父 George。感谢你们在整个过程中对我的支持。无论我去你们家小住,还是去拜访,你们都让我觉得像在自己家里一样,我和 Georgia 每次吃得都很好。我要感谢 Georgia 的姊妹 Anna 和 Kathy。每次回到家和大家一起放松休息的时候总是很开心,谢谢你们提供了一个让我和 Georgia 暂时放下本书的写作从巴尔的摩去和你们聚会的机会。

我要感谢我的编辑 Jonathan Gennick,没有他就不会有本书。Jonathan,你理应因为本书收获排山倒海的赞誉。作为本书的编辑,你的付出超越了一个编辑通常的工作范畴,非常感谢你。从提供素材,到无数次的重写,即使面临即将到来的最后期限也要保持气氛幽默愉快,没有你我不可能完成本书。我很荣幸你是我的编辑,也很感激你给我的这个机会。你既是一名经验丰富的 DBA,又是一位作者,很高兴能和你这样的技术专家合作。我不知道有多少编辑能够放下他们的工作改行做一名专业的 DBA,但我相信 Jonathan 可以。作为一位有 DBA 经验的编辑,你总是理解我要表达什么,即使有时候我不知道如何表达。O'Reilly 有你这样的员工真的很幸运,我也很幸运有你作为我的编辑。

我想感谢 Ales Spetic 和 Jonathan Gennick 的作品 Transact-SQL Cookbook。牛顿曾经说过:“如果说我看得比前人更远一点的话,那是因为我站在巨人的肩膀上。”在 Transact-SQL Cookbook 一书的致谢部分,Ales Spetic 写下的一段话是对这句名言的最好证明,我觉得应该放在每一本 SQL 书里。现在我把这段话也放在本书里。

我希望本书能为像 Joe Celko、David Rozenshtein、Anatoly Abramovich、Eugine Berger、Iztik Ben-Gan、Richard Snodgrass 等杰出作者的作品提供一点有益的补充。我花费了无数个夜晚研读他们的作品,我的知识几乎都源于他们的作品。当我写下这些内容的时候,我意识到,每当我花费一个晚上从他们的作品中学到点什么,他们当初必然花费了十个晚上用于将他们的知识凝结成连贯、可读的形式。能够以本书来回馈 SQL 社区是我的荣幸。

我想感谢 Mastering Oracle SQL 一书的作者 Sanjay Mishra,感谢他把我介绍给 Jonathan。如果不是 Sanjay,我可能不会认识 Jonathan,也就不可能写作本书了。一封简单的邮件就能改变生活,这多么地不可思议啊。我要感谢 David Rozenshtein,尤其要感谢他写了 The Essence of SQL,这本书教会我用集合和 SQL 的方式思考和解决问题。我想感谢 David Rozenshtein、Anatoly Abramovich 和 Eugene Birger,他们的作品 Optimizing Transact-SQL 教会了我很多现在仍然经常用到的高级 SQL 技巧。

我想感谢 Wireless Generation 公司的整个团队,这是一家优秀的公司,有着许多优秀的人才。非常感谢所有花时间来检查、讨论、提建议和帮助我完成本书的人:Jesse Davis、Joel Patterson、Philip Zee、Kevin Marshall、Doug Daniels、Otis Gospodnetic、Ken Gunn、John Stewart、Jim Abramson、Adam Mayer、Susan Lau、Alexis Le-Quoc 以及 Paul Feuer。我想感谢 Maggie Ho,她仔细检查了我的书稿,并且针对附录 A 给予了非常有用的反馈。我想感谢 Chuck Van Buren 和 Gillian Gutenberg,他们给了我很有益的关于跑步的建议。每天早上的锻炼让我思路清晰、精神放松。如果不是每天晨练,我想我可能无法完成本书。我想感谢 Steve Kang 和 Chad Levinson,当他们经过白天的辛苦工作,晚上想去 Union Square 的 Heartland Brewery 喝一杯或者吃烧烤的时候,却不得不和我没完没了地讨论各种 SQL 技巧。我想感谢 Aaron Boyd 给予我的支持和善意帮助,最重要的还有他的那些好建议。Aaron 是一个真诚、努力、直爽的人,公司因为有了他而变得更优秀。我想感谢 Olivier Pomel 给予的支持和帮助,尤其是那个根据若干行数据创建列表的 DB2 解决方案。Olivier 贡献了那个解决方案给我,我甚至无须再找个 DB2 系统来做测试!我向他解释了 WITH 子句的工作原理,几分钟后他就拿出了你在本书中看到的那个解决方案。

Jonah Harris 和 David Rozenshtein 还帮忙检查了手稿,并且提供了有益的反馈意见。ArunMarathe、Nuno Pinto do Souto 和 Andrew Odewahn 参与了提纲和实例选择的讨论,而在那个时候本书尚未成形。非常感谢所有人。

我想感谢 John Haydu 和 Oracle 公司的 MODEL 子句开发团队,他们花时间检查了我为 O'Reilly 写的关于 MODEL 子句的文章,最终他们帮助我更深刻地理解了 MODEL 子句的工作原理。我想感谢 Oracle 公司的 Tom Kyte,他允许我将他的 TO_BASE 函数改写成一个纯 SQL 解决方案。微软公司的 Bruno Denuit 回答了我关于 SQL Server 2005 引入的窗口函数的问题。PostgreSQL 的 Simon Riggs 告诉了我 PostgreSQL 中的 SQL 新特性(非常感谢你,Simon。你让我知道哪些新特性会在什么时间发布出来,让我能在本书中收录其中的一部分,比如非常酷的 GENERATE_SERIES 函数,我认为这个函数相较于数据透视表提供了更优雅的解决方案)。

最后,同样重要的是,我想感谢 Kay Young。当你才华横溢、对自己的工作充满激情的时候,你发现了同样才华横溢、充满激情的人,和这样的人一起工作简直太棒了。你在本书中看到的许多实例都来自于 Wireless Generation 公司每天遇到的实际问题。Kay 是我的同事,我们一起创造了许多解决方案。Kay,我想感谢你,并让你知道我十分感激你在整个过程中给我的帮助。你给我提建议,帮我做语法纠错,还帮我写代码,你在本书的写作过程中发挥了至关重要的作用。和你一起工作太棒了,因为你,Wireless Generation 会成为更好的公司。

——Anthony Molinaro

2005 年 9 月

目录

  • 版权声明
  • O'Reilly Media, Inc. 介绍
  • 前言
  • 第 1 章 检索记录
  • 第 2 章 查询结果排序
  • 第 3 章 多表查询
  • 第 4 章 插入、更新和删除
  • 第 5 章 元数据查询
  • 第 6 章 字符串处理
  • 第 7 章 数值处理
  • 第 8 章 日期运算
  • 第 9 章 日期处理
  • 第 10 章 区间查询
  • 第 11 章 高级查询
  • 第 12 章 报表和数据仓库
  • 第 13 章 层次查询
  • 第 14 章 杂项
  • 附录 A 窗口函数简介
  • 附录 B 重温 Rozenshtein
  • 作者简介
  • 封面介绍