内容简介
《锋利的SQL(第2版)》从基础、开发、性能调整和实战4方面介绍了SQL技术及其应用,包括数据库与架构、表管理、索引管理、基本查询、子查询、联接和APPLY运算符、操作结果集、窗口计算和表旋转、数据修改、视图、游标、存储过程、触发器、用户自定义函数、事务处理、并发访问控制、查询的优化与执行等内容。
《锋利的SQL(第2版)》既覆盖了改善效率和性能的普通SQL技术,也深入探讨了SQL新技术,更包含一些实用的查询解决方案,希望本书能够成为引领读者进入SQL查询殿堂的捷径。此外,本书在介绍各种查询语法时,更注重对查询逻辑思维方式的引导和介绍,帮助读者举一反三,提升动手解决实际问题的能力。
作者简介
张洪举, 微软公司最有价值专家(MVP),微软TechNet技术资源专栏作家。拥有近20年的数据库产品应用开发经历,尤其是在多层架构的面向对象系统分析设计、数据库分析设计、商业智能系统分析设计等方面积累了丰富的项目经验。著有《SQL Server 2005:入门、进阶与应用实例》等原创著作,并翻译了《SQL Server 2012 T-SQL基础教程循序渐进》等作品。
王晓文, 曲阜师范大学数学科学学院学生,虽学的是数学与应用数学专业,但个人喜欢研究数据库基础理论,在本书写作过程中主要负责SQL理论基础、数据库物理存储等相关内容的编写。
目录
基 础 篇
第1章 SQL简介 2
1.1 SQL的历史起源 2
1.1.1 CODASYL 3
1.1.2 IMS 3
1.1.3 RDBMS和SQL 3
1.1.4 ANSI和SQL方言 5
1.2 SQL的理论基础 5
1.2.1 集合理论 5
1.2.2 谓词逻辑 6
1.2.3 关系模型 7
1.3 Transact-SQL语言的类型 10
1.3.1 DDL语句 11
1.3.2 DML语句 12
1.3.3 编程和流控制语句 12
1.3.4 SQL语句的批处理 15
1.4 Transact-SQL语法 16
1.4.1 标识符 16
1.4.2 数据类型 17
1.4.3 函数 20
1.4.4 表达式 20
1.4.5 运算符 21
1.4.6 注释 21
1.4.7 保留关键字 22
1.5 常量和变量 22
1.5.1 常量 22
1.5.2 变量 24
1.6 运算符 26
1.6.1 算术运算符 26
1.6.2 赋值运算符 28
1.6.3 位运算符 28
1.6.4 比较运算符 29
1.6.5 逻辑运算符 30
1.6.6 字符串串联运算符 31
1.6.7 一元运算符 31
1.7 常用函数 31
1.7.1 聚合函数 31
1.7.2 配置函数 33
1.7.3 游标函数 34
1.7.4 日期和时间函数 35
1.7.5 数学函数 37
1.7.6 数据类型转换函数 39
1.7.7 字符串函数 43
1.7.8 文本和图像函数 45
1.7.9 逻辑函数 46
1.8 查询工具 46
1.8.1 Management Studio 46
1.8.2 sqlcmd 47
1.9 SQL书写规范 48
1.9.1 大小写规范 49
1.9.2 使用空格 50
1.9.3 使用缩进 51
1.9.4 使用垂直空白道 51
1.9.5 使用分组 52
第2章 数据库与架构 53
2.1 SQL Server数据库基础 53
2.1.1 数据库的服务方式 53
2.1.2 数据库实例 54
2.1.3 架构与对象 55
2.1.4 数据库文件和文件组 56
2.2 创建数据库 58
2.2.1 CREATE DATABASE语句的语法格式 58
2.2.2 创建数据库示例 60
2.2.3 判断数据库是否已经存在 63
2.3 修改数据库 63
2.3.1 扩展数据库和文件 63
2.3.2 向数据库中添加、删除和修改文件组 64
2.3.3 收缩数据库和文件 65
2.3.4 设置数据库选项 68
2.3.5 重命名数据库 71
2.4 删除数据库 71
2.5 架构管理 72
2.5.1 创建架构 72
2.5.2 修改架构 74
2.5.3 移动对象到一个新架构中 75
2.5.4 删除架构 76
第3章 表管理 77
3.1 表的物理存储方式 77
3.1.1 数据页 77
3.1.2 区 78
3.2 创建表 79
3.2.1 创建基本表 79
3.2.2 使用NULL约束 79
3.2.3 使用默认约束和标识列 80
3.2.4 其他数据完整性设置 86
3.3 修改表 88
3.3.1 为表添加新列 89
3.3.2 修改表中的列 89
3.3.3 删除表中的列 91
3.4 重命名和删除表 91
3.5 临时表 92
3.5.1 本地表和全局表 92
3.5.2 表变量 93
3.6 内存优化表 93
第4章 索引管理 95
4.1 索引的基础知识 95
4.1.1 索引的类型 95
4.1.2 索引的特征 99
4.1.3 常规索引设计规则 99
4.2 创建索引 101
4.2.1 最大索引限制 101
4.2.2 限制索引参与的数据类型 101
4.2.3 创建聚集索引 102
4.2.4 创建非聚集索引 103
4.2.5 创建具有包含性列的索引 104
4.2.6 为计算列创建索引 105
4.3 修改索引 107
4.3.1 禁用索引 107
4.3.2 重新组织和重新生成索引 108
4.3.3 设置索引选项 110
4.3.4 重命名索引 111
4.4 删除索引 111
第5章 基本查询 112
5.1 基本的SELECT语句 112
5.1.1 SELECT语句的结构 112
5.1.2 数据库对象的引用规则 114
5.2 使用选择列表和表别名 115
5.2.1 选择所有列 115
5.2.2 选择特定列 115
5.2.3 使用常量、函数和表达式 116
5.2.4 用表别名 121
5.3 使用WHERE子句筛选行 121
5.3.1 用比较搜索条件 121
5.3.2 使用范围搜索条件 123
5.3.3 使用列表搜索条件 123
5.3.4 使用模式匹配搜索条件 125
5.3.5 使用NULL比较搜索条件 127
5.4 使用GROUP BY子句进行分组计算 129
5.5 使用HAVING子句从中间结果筛选行 131
5.6 使用ORDER BY子句进行排序 132
5.6.1 指定排序列 132
5.6.2 指定排序顺序 133
5.6.3 指定排序规则 134
5.7 使用TOP和OFFSET-FETCH限制结果集 137
5.7.1 使用TOP选项 137
5.7.2 使用OFFSET-FETCH筛选 139
5.7.3 使用SET ROWCOUNT语句 140
5.8 使用DISTINCT消除重复行 140
5.9 同时操作 142
5.10 查询的逻辑处理 143
5.10.1 逻辑处理过程简介 143
5.10.2 步骤1:FROM 147
5.10.3 步骤2:使用WHERE筛选器 151
5.10.4 步骤3:数据分组 153
5.10.5 步骤4:使用HAVING筛选器 154
5.10.6 步骤5:处理SELECT列表 154
5.10.7 步骤6:使用ORDER BY子句 155
5.10.8 步骤7:使用TOP或OFFSET/
FETCH 156
第6章 子查询 157
6.1 在选择列表中使用子查询 157
6.1.1 子查询示例 157
6.1.2 子查询与联接的关系 159
6.2 含有IN和EXISTS的子查询 159
6.2.1 含有IN的子查询 159
6.2.2 含有EXISTS的子查询 161
6.2.3 含有NOT IN和NOT EXISTS的子查询 162
6.3 含有比较运算符的子查询 164
6.4 使用ANY、SOME或ALL关键字 165
6.5 使用多层嵌套子查询 168
6.6 子查询应遵循的规则 168
第7章 联接和APPLY运算符 172
7.1 联接的基本知识 172
7.1.1 联接的语法格式 172
7.1.2 联接所使用的逻辑处理阶段 173
7.1.3 列名限定和选择列表的使用 174
7.1.4 联接条件设定 175
7.2 交叉联接 175
7.2.1 交叉联接的语法格式 176
7.2.2 使用交叉联接查询全部数据 176
7.2.3 使用交叉联接优化查询性能 179
7.2.4 为交叉联接添加WHERE子句 180
7.3 内部联接 181
7.3.1 内部联接的语法格式 181
7.3.2 等值内部联接 182
7.3.3 不等值联接 184
7.4 外部联接 186
7.4.1 外部联接的语法格式 186
7.4.2 左外部联接 187
7.4.3 右外部联接 190
7.4.4 完全外部联接 191
7.5 自联接 192
7.5.1 使用不同列实现自联接 192
7.5.2 使用同一列实现自联接 193
7.6 多表联接 194
7.6.1 顺序联接 194
7.6.2 嵌套联接 196
7.6.3 指定联接的物理顺序 198
7.6.4 多表联接示例 199
7.7 联接算法 202
7.7.1 嵌套循环联接 202
7.7.2 合并联接 203
7.7.3 哈希联接 204
7.7.4 使用联接提示强制联接策略 206
7.8 使用APPLY运算符 208
第8章 操作结果集 211
8.1 合并结果集 211
8.1.1 UNION与UNION ALL 212
8.1.2 使用ORDER BY子句 213
8.1.3 结果集的合并顺序 214
8.2 查询结果集的差异行 214
8.2.1 使用EXCEPT运算符 214
8.2.2 查询全部差异行 216
8.3 查询结果集的相同行 218
8.3.1 使用INTERSECT运算符 218
8.3.2查
前言/序言
《代码的语言:构建高效、健壮的软件系统》 引言: 在当今数字时代,软件系统如同一座座庞大的城市,承载着我们的信息、连接着世界、驱动着经济。而这座城市的核心,是构建这些建筑物的“代码”。《代码的语言》深入探索了编写高质量代码的艺术与科学,旨在帮助开发者掌握构建真正高效、健壮、可维护的软件系统的核心原则和实践。这本书并非聚焦于某种特定的编程语言或框架,而是着眼于软件开发中最本质、最普适的理念,助你成为一名更优秀的工程师,无论你身处何种技术栈。 第一部分:清晰的表达——代码的基石 变量的哲学:命名与意图 命名之道: 好的命名远不止是给变量起个名字,它是代码中最直接的沟通方式。我们会深入探讨如何命名才能清晰地传达变量的用途、含义和生命周期。从简洁明了的单字母命名(仅在特定上下文下)到富有描述性的长名称,从遵循约定俗成的命名规范(如驼峰命名法、蛇形命名法)到避免歧义、模糊不清的命名,本书都将提供详实的案例和指导。我们将探讨命名时需要考虑的因素,如变量的作用域、数据类型、以及它在算法中的角色。例如,一个表示用户ID的变量,是应该命名为`id`,还是`userId`,亦或是`customerIdentifier`?本书将引导你做出最恰当的选择,让代码“不言而喻”。 意图的传递: 变量的命名是表达意图的直接体现。我们不仅要让变量名符合规范,更要让它准确地反映开发者在编写这段代码时的思考过程和业务逻辑。本书将鼓励开发者跳出“能跑就行”的思维,深入思考变量在整个程序流程中的意义,以及它如何与其他变量、函数协同工作。我们将讨论如何通过命名来预设和传达变量的状态(如`isLoggedIn`、`hasError`),以及如何通过命名来区分不同层级的抽象(如`User`对象与`UserData`对象)。 函数的艺术:职责与契约 单一职责原则: 函数是代码的基本单元,每一个函数都应该有且仅有一个清晰的职责。本书将详细阐述单一职责原则的重要性,并提供判断函数是否承担过多责任的方法。过大的函数不仅难以理解,更难以测试和复用。我们将通过重构的实例,展示如何将臃肿的函数分解为更小、更专注的单元,从而提升代码的可读性和可维护性。 清晰的接口: 函数的输入(参数)和输出(返回值)构成了它的接口。一个好的函数接口应该具有清晰的定义,明确的约束和可预测的行为。本书将指导开发者如何设计参数列表,避免过多的参数;如何选择合适的数据结构作为参数和返回值;以及如何利用返回值来清晰地表达函数的执行结果(成功、失败、特定状态)。我们将讨论“无副作用”函数的概念,以及如何通过设计来最小化函数之间的耦合,让代码更容易被理解和替换。 文档的承诺: 函数的注释不是可有可无的装饰,而是对使用者的一种承诺。本书强调编写清晰、准确、及时的函数文档的重要性。文档应该解释函数的用途、参数的含义、返回值的意义、可能抛出的异常,以及其行为的任何特殊注意事项。我们将探讨文档的最佳实践,包括如何描述复杂逻辑、如何记录前置条件和后置条件,以及如何让文档与代码保持同步。 数据结构的智慧:组织与映射 选择的基石: 数据结构是组织和存储数据的载体,正确的选择能够极大地影响程序的效率和可读性。本书将回顾和深入探讨常见的、基础的数据结构,如数组、链表、栈、队列、哈希表、树(二叉树、平衡树)、图等。我们不仅会介绍它们的结构和基本操作,更重要的是,将引导开发者理解它们各自的优缺点、时间与空间复杂度,以及在不同场景下的适用性。 映射现实世界: 优秀的数据结构设计能够有效地映射现实世界的业务逻辑。本书将通过大量实例,展示如何根据实际问题来选择或设计合适的数据结构。例如,如何用哈希表来快速查找用户信息?如何用树来表示层级关系?如何用图来建模网络连接?我们还将探讨如何通过组合基本数据结构来构建更复杂、更强大的数据组织方式。 第二部分:代码的健壮性——抵御风雨的基石 异常的哲学:预见与响应 优雅的处理: 错误和异常是软件开发中不可避免的一部分。本书将深入探讨如何有效地处理异常,将“错误”转化为“可控事件”。我们不仅会介绍try-catch-finally等语言特性,更重要的是,将引导开发者理解异常的分类、异常的传播机制,以及如何设计出既能捕获到必要信息,又能优雅地响应错误的程序。 自定义异常: 在某些情况下,使用语言内置的异常类型可能不足以清晰地表达业务场景中的特定错误。本书将指导开发者如何创建自定义异常类型,以更准确地描述问题,并为上层调用者提供更明确的错误信息。我们将探讨自定义异常的设计原则,如何继承、如何添加额外的信息,以及如何让自定义异常成为代码清晰度的助力而非负担。 避免“吞噬”异常: “吞噬”异常,即捕获异常后不做任何处理,是导致程序行为难以预测的常见原因。本书将强调记录异常、向上抛出或进行有意义的处理的重要性,防止程序在默默地失效。 测试的守护:验证与信心 单元测试的意义: 单元测试是保证代码质量的基石。本书将强调单元测试的重要性,并提供编写高质量单元测试的实践指南。我们将介绍测试驱动开发(TDD)的基本理念,以及如何编写能够快速反馈、覆盖关键逻辑的测试用例。 测试的覆盖面: 除了正常路径,我们还需要测试各种边界条件、异常情况和错误输入。本书将指导开发者如何设计全面的测试场景,确保代码在各种情况下都能正常工作。我们将讨论如何编写“健壮”的测试,即使代码实现发生变化,测试也能在一定程度上保持有效。 集成测试的维度: 当多个模块组合在一起时,新的问题可能会浮现。本书将介绍集成测试的概念,以及如何通过集成测试来验证不同组件之间的交互是否正确。我们将探讨不同类型的集成测试,以及如何选择适合项目的测试策略。 并发与并行:驾驭多线程的艺术 理解核心概念: 在多核处理器日益普及的今天,编写高效的并发和并行代码至关重要。本书将深入浅出地解释并发与并行的基本概念,如线程、进程、锁、互斥量、信号量、死锁、活锁等。我们将帮助开发者建立起对这些概念清晰的理解,为后续的学习打下坚实的基础。 避免常见的陷阱: 并发编程是开发中最容易出错的领域之一。本书将重点剖析并发编程中常见的陷阱,如竞态条件(Race Condition)、死锁、饥饿等,并提供行之有效的规避方法和设计模式。我们将通过具体的代码示例,演示如何安全地共享数据、如何进行线程同步,以及如何设计出高内聚、低耦合的并发模块。 异步编程的优势: 随着事件驱动模型和非阻塞 I/O 的发展,异步编程已成为提升系统吞吐量和响应能力的重要手段。本书将介绍异步编程的核心思想,如回调、Promise、async/await 等,并指导开发者如何利用异步机制来编写更高效、更具扩展性的应用程序。 第三部分:代码的进化——适应与发展 重构的实践:持续改进 “代码腐烂”的应对: 随着时间的推移,代码会因为需求变更、维护不当而逐渐“腐烂”,可读性和可维护性下降。本书将介绍“代码腐烂”的常见表现,以及如何通过持续的重构来保持代码的健康。 安全的重构技巧: 重构的目的是在不改变程序外在行为的前提下,改进其内部结构。本书将提供一系列安全、有效的重构技巧,如提取函数、移动函数、替换继承为委托、引入参数对象等。我们将强调测试在重构过程中的重要性,确保每一次改动都不会引入新的bug。 设计模式的智慧:复用经验 解决常见问题: 设计模式是前人在软件开发中总结出的解决特定问题的通用方案。本书将介绍一些经典且实用的设计模式,如工厂模式、单例模式、观察者模式、策略模式、装饰者模式等。 模式的正确使用: 我们将不仅仅是罗列模式,更重要的是引导开发者理解每个模式的适用场景、解决的问题以及它所带来的优缺点。本书将强调避免过度设计和滥用设计模式,鼓励开发者在真正需要时才引入,以提升代码的可读性和可维护性。 代码的可读性:沟通的艺术 “写给人看”的代码: 代码首先是写给人看的,然后才是给机器执行的。本书将从多个角度阐述如何写出易于阅读和理解的代码。除了前面提到的命名和函数设计,我们还将关注代码的结构、格式、缩进、空行等视觉元素,以及如何利用它们来增强代码的可读性。 “ DRY ”原则的应用: “Don't Repeat Yourself”(不要重复自己)是提高代码效率和可维护性的重要原则。本书将深入探讨DRY原则的内涵,并提供多种实现方式,如通过函数、类、模块等来消除重复的代码,从而减少维护成本,降低引入bug的风险。 结语: 《代码的语言》不仅仅是一本关于编程技术的书,更是一本关于如何思考、如何构建、如何迭代的指南。它鼓励开发者培养严谨的思维方式,注重细节,拥抱变化,并不断追求卓越。通过掌握本书中的理念和实践,你将能够编写出更加高效、健壮、易于维护的软件系统,在不断变化的软件开发领域中,走得更远、更稳健。愿这本书成为你成为一名优秀工程师的有力伙伴。