《SQL 必知必会》- 阅读索引

阅读总结

用于了解 SQL 的基础知识还是非常好的。

在使用时可以具体看 DBMS 文档。

以下 SQL 以 cnpmjs.org 数据库为例。


[toc]


第 1 课 了解 SQL

1.1 数据库基础

1.1.1 数据库

  • 数据库(database):保存有组织的数据的容器(通常是一个文件或一组文件)
  • DBMS,数据库管理系统(即数据库软件)

1.1.2 表

  • 表(table):某种特定类型数据的结构化清单(一种结构化的文件)
    • 统一类型的数据
    • 表名在数据库中唯一
  • 模式(schema):关于数据库和表的布局及特性的信息

1.1.3 列

  • 列(column):表中的一个字段
  • 数据类型(datatype):所允许的数据的类型,每个表列都有相应的数据类型,它限制(或允许)该列中存储的数据
    • 帮助分类数据
    • 优化磁盘使用
    • 数据类型兼容问题(不同的 DBMS)

1.1.3 行

  • 行(row):表中的一个记录(record)

1.1.5 主键

  • 主键(primary key):一列(或一组列),其值能够唯一标识表中的每一行
    • 任何满足条件的列都可以作为主键
      • 唯一标识行
      • 不允许 NULL
      • 不允许修改或更新
      • 删除后其值不能重用
    • 不是必须,也可以是多列主键

1.2 什么是 SQL

  • SQL:结构化查询语言(Structured Query Language)
    • 不是某个 DBMS 专用的
    • 简单易学,强描述性
    • 强大
  • SQL 的扩展:针对特定 DBMS 的简化特定操作执行的方法
  • 标准 SQL 由 ANSI 标准委员会管理

1.3 动手实践

1.4 小结

  • 什么是 SQL
  • 为什么很有用
  • 基本的数据库术语

第 2 课 检索数据

2.1 SELECT 语句

  • 关键词(keyword):作为 SQL 组成部分的保留字,不能用作表或列的名字
  • SELECT 语句:从一个或多个表中检索信息
    • 想选择什么,以及从什么地方选择

2.2 检索单个列

SELECT name FROM USER
  • 数据未排序(可能是被添加到表中的属性,可能不是)
  • 多条 SQL 必须以 ; 分隔
  • SQL 语句不区分大小写
  • 所有空格都被忽略

2.3 检索多个列

SELECT id, name FROM user
  • 列名之间 , 分隔

2.4 检索所有列

SELECT * FROM user
  • 使用通配符 *
    • 耗费性能
    • 可以检索未知列

2.5 检索不同的值

SELECT DISTINCT name FROM module
  • 使用 DISTINCT 关键字
    • 必须直接放在列名前
    • 作用于所有的列

2.6 限制结果

# MySQL、MariaDB、PostgreSQL、SQLite
SELECT name from user LIMIT 10

# 第 5 行后的 10 行
SELECT name from user LIMIT 10 OFFSET 5
  • 不同的 DBMS 不同
  • LIMITOFFSET

2.7 使用注释

SELECT name FROM user -- 注释
# 注释
/*
 * 注释
 */
  • -- 行内
  • # 单行
  • /* */ 多行

2.8 小结

  • SELECT 语句检索
  • 注释

第 3 课 排序检索数据

3.1 排序数据

SELECT name from user ORDER BY name
SELECT name from user ORDER BY id
  • 关系数据库设计理论认为:如果不明确规定排序顺序,则不应该假定检索出的数据的顺序有任何意义
  • 子句(clause):SQL 语句由子句构成,一个子句通常由一个关键字加所提供的数据组成
  • ORDER BY
    • ORDER BY 子句要求在 SELECT 语句的最后一句
    • 可以通过非检索列排序

3.2 按多个列排序

SELECT id, name, email from user ORDER BY name, id
  • 按优先级以逗号分隔

3.3 按列位置排序

SELECT id, name, email from user ORDER BY 2, 3
# =
SELECT id, name, email from user ORDER BY name, email
  • 用所选列的序号代替重复列名
  • 不建议使用

3.4 指定排序方向

# 先按 name 降序,再按 email 升序
SELECT id, name, email from user ORDER BY name DESC, email
  • 使用 DESC 关键字
    • 只应用于直接位于前面的列名

3.5 小结

  • ORDER BY 子句

第 4 课 过滤数据

4.1 使用 WHERE 子句

SELECT * from user WHERE name = 'xiaoming'
  • WHERE 指定搜索条件(过滤条件)
    • WHERE 子句放在表名(FROM 子句)后
  • 使用 SQL 过滤而不是应用过滤

4.2 WHERE 子句操作符

  • 操作符(不同 DBMS 不同)
操作符 说明
=
<> 不等于
!= 不等于
<
<=
>
>=
BETWEEN ... AND ...
IS NULL

4.2.1 检查单个值

SELECT * from user WHERE id < 10

4.2.2 不匹配检查

SELECT * from user WHERE npm_user <> 1
  • 有的 DBMS 不支持 !=

4.2.3 范围值检查

SELECT * from user WHERE id BETWEEN 10 AND 100

4.2.3 空值检查

SELECT * from user WHERE email IS NULL
  • IS NULL 子句
  • NULL,无值

4.3 小结

  • WHERE 子句过滤

第 5 课 高级数据过滤

5.1 组合 WHERE 子句

  • 操作符(operator,逻辑操作符):用来联结或改变 WHERE 子句中的子句的关键字

5.1.1 AND 操作符

SELECT *
FROM user
WHERE npm_user = 1 AND email IS NULL
  • AND:用在 WHERE 子句中的关键字,用来表示检索满足所有给定条件的行

5.1.2 OR 操作符

  • OR:用在 WHERE 子句中的关键字,用来表示检索匹配任一给定条件的行

5.1.3 求值顺序

# npm_user = 0 或 1,同时 email 为空值
SELECT *
FROM user
WHERE (npm_user = 1 OR npm_user = 0) AND email IS NULL
  • SQL 会优先处理 AND 操作符
  • 使用 () 来控制优先级
  • 建议 AND OR 混用时始终使用圆括号

5.2 IN 操作符

SELECT * 
FROM module
WHERE authoer IN ('xiaoming', 'xiaohong')
ORDER BY name
  • IN:WHERE 子句中用来指定要匹配值的清单的关键字,功能与 OR 相当
    • 多选项时更直观
    • 与 AND/OR 组合时,求值顺序更易管理
    • 比一组 OR 执行更快
    • 最大优点是可以包含其他 SELECT 语句,能更动态地建立 WHERE 子句

5.3 NOT 操作符

SELECT *
FROM user
WHERE NOT (npm_user = 1 AND id > 10)
  • NOT:WHERE 子句中用来否定其后条件的关键字

5.4 小结

  • AND/OR
  • 求值顺序
  • NOT

第 6 课 用通配符进行过滤

6.1 LIKE 操作符

  • 通配符(wildcard):用来匹配值的一部分的特殊字符
    • 只能用于字符串
  • 搜索模式(search pattern):由字面值、通配符或两者组合构成的搜索条件
  • 谓词(predicate):LIKE 作为谓词

6.1.1 百分号(%)通配符

# xiao 开头的 name
SELECT * 
FROM user
WHERE name LIKE 'xiao%'
  • %:任何字符出现任意次数
    • 可以在搜索模式的不同位置:a%e
  • 根据 DBMS 的不同及其配置,可以区分大小写
  • 不匹配 NULL

6.1.2 下划线(_)通配符

# 两个字符的 163 邮箱
SELECT * 
FROM user
WHERE email LIKE '__@163.com'
  • _:匹配单个字符

6.1.3 方括号([])通配符

# 非 a 和 b 开头的 name
SELECT *
FROM user
WHERE name LIKE '[^ab]%'
  • []: 指定字符集,必须匹配指定位置的一个字符
    • 只有 Access 和 SQL Server 支持集合
  • ^:否定集合

6.2 使用通配符的技巧

  • 不过度使用,通配符是最后的手段
  • 不要用在搜索模式的开始处,否则会很慢
  • 注意通配符的位置,容易出错

6.3 小结

  • 什么是通配符
  • 如何在 WHERE 子句中使用
  • 注意点

第 7 课 创建计算字段

7.1 计算字段

  • 字段(field):与列基本相同,经常互换使用
  • 只有数据库知道 SELECT 语句中哪些是实际的表列,哪些是计算字段,对应用程序是透明的
  • 把数据转换和格式化工作放在数据库中会比在应用程序中更快

7.2 拼接字段

SELECT CONCAT(name, '[', email, ']') AS info
FROM user
WHERE id < 100
  • CONCAT
  • TRIM、LTRIM(清除左边的空格)、RTRIM(清除右边的空格)
  • AS:别名或导出列
    • 使用 AS 是最佳实践
    • 让应用程序可以按名称引用这个列
    • 可以对列中的不合法字符重新命名
    • 增加可读性

7.3 执行算术计算

SELECT dist_size / 1024 AS dist_kb
FROM module
LIMIT 100
  • SELECT 可以用来测试计算(省略 FROM),如 SELECT 3 * 2 返回 6

7.4 小结

  • 计算字段及如何创建
  • 字符串拼接
  • 算数计算
  • 别名

第 8 课 使用函数处理数据

8.1 函数

  • 提供数据的转化和处理方法
  • 各 DBMS 差异大,因此使用函数的 SQL 可移植性差

8.2 使用函数

  • 处理文本字符串
  • 数值算数操作
  • 处理日期和时间
  • 返回 DBMS 证使用的特殊信息(如用户登录信息)的系统函数

8.2.1 文本处理函数

SELECT UPPER(name) AS name_upcase, email
From user
  • 常用的文本处理函数
函数 说明
LEFT 返回字符串左边的字符
LENGTH 返回字符串长度
LOWER 返回字符串小写
UPPER 返回字符串大写
LTRIM 去字符串左边空格
RIGHT 返回字符串右边的字符
RTRIM 去字符串右边空格
SOUNDEX 返回字符串的 SOUNDEX 值(语音描述)

8.2.2 日期和时间处理函数

  • DBMS 都有自己的日期时间形式和处理函数
    • 高效排序或过滤
    • 节省存储空间
    • 可移植性差

8.2.3 数值处理函数

  • 常用数值处理函数
函数 说明
ABS 绝对值
COS 余弦值
EXP 指数值
PI 圆周率
SIN 正弦值
SQRT 平方根
TAN 正切

8.3 小结

  • 有用
  • 各 DBMS 不一致

第 9 课 汇总数据

9.1 聚集函数

  • 只需要汇总数据而不需要实际检索,如
    • 满足某个条件的行的行数
    • 某些行的和
    • 某些列的最大值、平均值等
  • 聚集函数(aggregate function):对某些行运行的函数,计算并返回一个值
  • 在各 DMBS 中实现相当一致
函数 说明
AVG 某列的平均值
COUNT 某列的行数
MAX 某列的最大值
MIN 某列的最小值
SUM 某列之和

9.1.1 AVG() 函数

SELECT AVG(dist_size) 
AS avg_dist_size 
FROM module

SELECT AVG(dist_size)
AS avg_babel_dist_size
FROM module
WHERE name LIKE '@babel/%'
  • 只能用于单列,多个列的平均值必须使用多个 AVG()
  • 对 NULL
    • 值为 NULL 的行会被忽略

9.1.1 COUNT() 函数

SELECT COUNT(*) AS user_count FROM user;
SELECT COUNT(email) AS user_with_email_count FROM user;
  • 对 NULL
    • COUNT(*) 对表中行的数目进行技术,不管列中是否包含 NULL
    • COUNT(column) 对特定列中非 NULL 值的行的数目进行计数

9.1.3 MAX() 函数

SELECT MAX(dist_size) AS max_dist_size FROM module
  • 对非数值
    • 返回按该列排序后的最后一行
  • 对 NULL
    • 值为 NULL 的行会被忽略

9.1.4 MIN() 函数

  • 与 MAX 相反

9.1.5 SUM() 函数

SELECT SUM(dist_size) AS total_dist_size FROM module
SELECT SUM(dist_size*LENGTH(name)) AS for_test FROM module
  • 对非数值忽略
  • 对 NULL 忽略

9.2 聚集不同的值

SELECT AVG(DISTINCT dist_size) 
AS avg_dist_size 
FROM module
  • 默认为 ALL,对所有行执行计算
  • 指定 DISTINCT 只包含不同的值

9.3 组合聚集函数

SELECT 
MAX(dist_size) AS max_dist_size,
MIN(dist_size) AS min_dist_size,
FROM module

9.4 小结

  • 使用 5 个聚集函数来汇总数据

第 10 课 分组数据

10.1 数据分组

  • 使用分组可以将数据分成多个逻辑组,对每个组进行聚集计算

10.2 创建分组

# 每个包及其版本数
SELECT name, COUNT(*) AS version_count 
FROM module 
GROUP BY name
  • GROUP BY 子句可以包含任意数目的列
  • 列可以是检索列或有效表达式(不能是聚集函数)
  • NULL 会被作为一个分组返回
  • 放在 WHERE 后,ORDER BY 前

10.3 过滤分组

# 版本数大于 100 的包及其版本数
SELECT name, COUNT(*) AS version_count 
FROM module 
GROUP BY name 
HAVING COUNT(*) >= 100
# 体积大于 1024 的版本数大于 100 的包及其版本
SELECT name, COUNT(*) AS version_count 
FROM module 
WHERE dist_size > 1024
GROUP BY name 
HAVING COUNT(*) >= 100
  • HAVING 语法同 WHERE,其能代替 WHERE
  • HAVING 过滤分组,WHERE 过滤行
  • HAVING 在数据分组后进行过滤,WHERE 在分组前进行过滤

10.4 分组和排序

  • ORDER BY 和 GROUP BY
ORDER BY GROUP BY
对产生的输出排序 对行分组,输出不一定是分组的顺序
任意列都可以使用 只能使用选择列或表达式列
不一定需要 在与聚集函数一起使用时必要

10.5 SELECT 子句顺序

  1. SELECT
  2. FROM
  3. WHERE
  4. GROUP BY
  5. HAVING
  6. ORDER BY

10.6 小结

  • 使用 GROUP BY 对多组数据汇总计算
  • HAVING 以及和 WHERE 的区别

第 11 课 使用子查询

11.1 子查询

  • 查询(query):任何 SQL 语句都是查询,但一般指 SELECT 语句
  • 子查询(subquery):嵌套在其他查询中查询

11.2 利用子查询进行过滤

# 内部作者的包
SELECT DISTINCT name
FROM module
WHERE author IN (
    SELECT name 
    FROM user 
    WHERE npm_user = 0
)
  • 子查询的 SELECT 语句只能查询单个列
  • 子查询存在性能问题,更有效方法见 12 章

11.3 作为计算字段使用子查询

# 每个有包的作者的包的数量
SELECT name, (
    SELECT COUNT(DISTINCT name) 
    FROM module
    WHERE module.author = user.name
) AS module_count
FROM user
HAVING module_count > 0
  • 通过 table.column 来完全限定列名,避免歧义
  • JOIN 是更好的解决方案

11.4 小结

  • 子查询及如何使用

第 12 课 联结表

12.1 联结

  • JOIN

12.1.1 关系表

  • 将数据分层多个关联的表

12.1.2 为什么使用联结

  • 联结是一种机制,用来在一条 SELECT 语句中关联表

12.2 创建联结

SELECT module.name, version, user.name, email
FROM module, user
WHERE module.author = user.name

12.2.1 WHERE 子句的重要性

  • 没有 WHERE,检索的结果是笛卡尔积(第一张表的行数乘以第二张表的行数),也叫做叉联结

12.2.2 内联结

  • 上面的例子是等值联结,也称为内联结
  • 可以用 INNER JOIN 语法
SELECT module.name, version, user.name, email
FROM module INNER JOIN user
ON module.author = user.name

12.2.3 联结多个表

  • 同两张表
  • 过多的联结表耗费性能

12.3 小结

  • 联结是 SQL 中最重要、最强大的特性
  • 要求对关系数据库设计有基本的了解

第 13 课 创建高级联结

13.1 使用表别名

SELECT m.name, version, u.name, email
FROM module AS m INNER JOIN user AS u
ON m.author = u.name

13.2 使用不同类型的联结

  • 内联结(等值联结)
  • 自联结(self join)
  • 自然联结(natural join)
  • 外联结(outer join)

13.2.1 自联结

# 和 babel 同作者的包
SELECT DISTINCT m1.name, m1.author
FROM module AS m1, module AS m2
WHERE m1.author = m2.author AND m2.name = 'babel'
  • 用来替代从相同表中检索数据的使用子查询的语句
  • 通常 DBMS 处理联结远比子查询快

13.2.2 自然联结

  • 联结中至少有一列会出现在多个表中,自然联结使每一列只返回一次
  • 以上的内联结都是自然联结

13.3.3 外联结

# 作者及其包
SELECT DISTINCT u.name, u.email, m.name
FROM user AS u LEFT OUTER JOIN module AS m
ON u.name = m.author
  • 需要包含没有关联行的那些行的联结
  • LEFT OUTER JOIN、RIGHT OUTER JOIN、FULL OUTER JOIN(mysql 等不支持)

13.3 使用带聚集函数的联结

# 所有没有发过包的用户
SELECT u.name, u.email, COUNT(DISTINCT m.name) AS module_count
FROM user AS u LEFT OUTER JOIN module AS m
ON u.name = m.author
GROUP BY u.name
HAVING module_count < 1

13.4 使用联结和联结条件

  • 注意使用的联结类型,应使用内联结还是外联结
  • 注意各 DBMS 的联结语法
  • 使用正确的联结条件
  • 多表联结合法且有用,注意使用前分开测试

13.5 小结

  • 联结类型
  • 联结中聚集
  • 注意事项

第 14 课 组合查询

14.1 组合查询

  • 并(union):执行多个查询(多条 SELECT 语句)并将结果作为一个查询结果集返回,也称为复合查询
  • 使用场景
    • 在一个查询中从不同的表返回结构数据
    • 对一个表执行多个查询,按一个查询返回结果
  • 具有多个 WHERE 子句的 SELECT 语句都可以作为一个组合查询

14.2 创建组合查询

14.2.1 使用 UNION

SELECT name FROM user WHERE npm_user = 0
UNION
SELECT name FROM user WHERE email IS NULL
# 等于
SELECT name FROM user WHERE npm_user = 0 OR email IS NULL
  • 多数 DBMS 会将 UNION 优化为一条 SELECT 语句

14.2.2 UNION 规则

  • 必须由两条及以上 SELECT 语句组成,语句之间用 UNION 分隔
  • 每个查询必须包含相同的列、表达式或聚集函数
  • 列数据类型必须兼容

14.2.3 包含或取消重复的行

  • UNION 会过滤重复的行
  • UNION ALL 包含重复的行,这是 WHERE 无法完成的

14.2.4 对组合查询结果排序

  • ORDER BY 放在所有 SELECT 之后

14.3 小结

  • UNION 组合查询语句

第 15 课 插入数据

15.1 数据插入

  • 插入数据的方式
    • 完整的行
    • 行的一部分
    • 某些查询的结果
  • DBMS 中的 INSERT 权限

15.1.1 插入完整的行

# 插入用户,可省略 allow null 列或有默认值的列
INSERT INTO user
(name, email, npm_user)
VALUES
('1', '2', 0)

15.1.2 插入部分行

  • 如 15.1.1
  • 省略的列必须满足条件之一
    • 允许为 NULL
    • 表定义中有默认值

15.1.3 插入检索出的数据

# 从 user2 中检索出 n 行数据插入 user
INSERT INTO user(name, email, npm_user)
SELECT name, email, npm_user FROM user2
  • SELECT 后的列的位置与 INSERT 对应,而无须列名一致

15.2 从一个表复制到另一个表

# 创建新表 user2,并复制 user 数据
SELECT * INTO user2 FROM user
# mysql、sqllite 等
CREATE TABLE user2 AS
SELECT * FROM user
  • 任何 SELECT 选项和子句都可以使用,包括 WHERE、GROUP BY
  • 可用联结
  • 只能插入到一张表中

15.3 小结

  • 使用 INSERT 将行插入到数据库表中

第 16 课 更新和删除数据

16.1 更新数据

UPDATE user
SET email = NULL,
SET npm_user = 0
WHERE name = 'a'
  • UPDATE
    • 更新特定行,注意 WHERE
    • 更新所有行
  • DBMS 中的 UPDATE 权限
  • UPDATE 语句组成
    • 要更新的表
    • 列名和它们的新值
    • 行的过滤条件

16.2 删除数据

DELETE FROM user
WHERE name = 'a'
  • DELETE
    • 删除特定行,注意 WHERE
    • 删除所有行
  • DBMS 中的 DELETE 权限
  • 删除的是行而不是表
  • 使用 TRUNCATE TABLE 来删除所有行更快(因为不记录数据的变动)

16.3 更新和删除的指导原则

  • 除非确实打算更新和删除每一行,否则必须带 WHERE
  • 保证每个表都有主键
  • 先用 SELECT 测试,保证正确
  • 使用强制实施引用完整性的数据库
  • 如果 DBMS 支持,施加约束不允许执行不带 WHERE 的

16.4 小结

  • UPDATE、DELETE 的使用
  • 保证数据安全应该遵循的一些指导原则

第 17 课 创建和操纵表

17.1 创建表

  • 用交互式创建和管理表的工具
  • 用 SQL 语句创建

17.1.1 表创建基础

CREATE TABLE test (
    a CHAR(10) NOT NULL,
    b VARCHAR(1000) NOT NULL,
    c BIGINT(10) NOT NULL
)

17.1.2 使用 NULL 值

# 允许 b 为 NULL
CREATE TABLE test (
    a CHAR(10) NOT NULL,
    b VARCHAR(1000),
    c BIGINT(10) NOT NULL
)
  • 每个表列要么是 NULL 列,要么是 NOT NULL 列
  • 允许 NULL 值的列不能作为主键
  • 有的 DBMS 需要指定关键词 NULL

17.1.3 指定默认值

CREATE TABLE test (
    a CHAR(10) NOT NULL DEFAULT 1,
    b VARCHAR(1000),
    t DATE NOT NULL DEFAULT CURRENT_DATE()
)

17.2 更新表

ALTER TABLE test
ADD d CHAR(20);
ALTER TABLE test
DROP COLUMN a;
  • 对 ALTER TABLE 的限制
    • 尽量避免在表中包含数据时更新
    • 所有 DBMS 都支持增加列
    • 许多 DBMS 不允许删除或更改列
    • 多少 DMBS 允许重命名列
    • 许多 DBMS 限制对有数据的列更改,对无数据的列几乎没有限制
  • 复杂的表结构更改一般需要手动删除过程
    1. 用新的列布局创建一个新表
    2. 用 INSERT SELECT 从旧表复制数据倒新表
    3. 检验包含所需数据的新表
    4. 重命名旧表(可以删除)
    5. 用旧表原来的名字重命名新表
    6. 重新创建触发器、存储过程、索引和外键

17.3 删除表

DROP TABLE test;
  • 使用关系规则防止意外删除,如果是某个关系的组成部分,DBMS 会阻止删除

17.4 重命名表

  • 不同 DBMS 有差别,MySQL 用 RENAME

17.5 小结

  • CREATE TABLE
  • ALTER TABLE
  • DROP TABLE
  • RENAME TABLE

第 18 课 使用视图

18.1 视图

  • 视图是虚拟的表,只包含使用时动态检索数据的查询
  • 可以用与表基本相同的方式使用
  • 本身不包含数据
  • 每次使用视图时,都必须处理传执行时所需要的所有检查,因此比较耗费性能

18.1.1 为什么使用视图

  • 重用 SQL 语句
  • 简化复制的 SQL 操作。在编写查询后,可以方便地重用它而不必知道基本查询细节
  • 使用表的一部分而不是整个表
  • 保护数据。可以授予用户访问表的特定部分的权限
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据

18.1.2 视图的规划和限制

  • 视图命名唯一
  • 不限制视图数量
  • 创建视图的权限
  • 视图嵌套
  • 实体不能所有,也不能有关联的触发器或默认值
  • 其它参与具体的 DBMS 文档

18.2 创建视图

  • CREATE VIEW
  • DROP VIEW

18.2.1 利用视图简化复杂的联结

CREATE VIEW user_module AS
SELECT user.name , user.email, module.name as module
FROM user INNER JOIN module
on user.name = module.author;

18.2.2 用视图重新格式化检索出的数据

18.2.3 用视图过滤不想要的数据

18.2.4 使用视图与计算字段

18.3 小结

  • 视图为虚拟的表,包含的不是数据而是根据需要检索数据的查询

第 19 课 使用存储的过程

19.1 存储过程

  • 存储过程是为以后使用而保存的一条或多条 SQL 语句。可视为批文件(不仅限于此)

19.2 为什么要使用存储过程

  • 好处:简单、安全、高性能
    • 把处理封装在一个易用的单元,简化复杂的操作
    • 使用同一存储过程,可以保证数据的一致性
    • 隔离变动
    • 存储过程通常以编译过的形式存储,简化了 DBMS 处理工作,提高了性能
    • 可以使用一些职能用在单个请求中的 SQL 元素和特性
  • 缺陷
    • DBMS 语法差异,几乎不可移植
    • 编写存储过程复杂
  • 编写存储过程权限

19.3 执行存储过程

  • EXECUTE + 存储过程名 + 参数

19.4 创建存储过程

19.5 小结

  • 见具体 DBMS

第 20 课 管理事务处理

20.1 事务处理

  • 事务处理(transaction processing):通过确保成批的 SQL 操作要么完全执行,要么完全不执行,来维护数据库的完整性
  • 几个术语
    • 事务(transaction):一组 SQL 语句
    • 回退(rollback):撤销指定 SQL 语句的过程
    • 提交(commit):将未存储的 SQL 语句结果写入数据库表
    • 保留点(savepoint):事务处理中设置的临时占位符(placeholder),可以对它发布回退(与回退整个事务处理不同)
  • 可以回退:INSERT、UPDATE、DELETE 语句

20.2 控制事务处理

# MySQL
START TRANSACTION
# ...
  • 管理事务的关键在于将 SQL 语句组成分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退

20.2.1 使用 ROLLBACK

# 使用 ROLLBACK 撤销
DELETE FROM test;
ROLLBACK;

20.2.2 使用 COMMIT

# SQL server
BEGIN TRANSACTION;
DELETE ...
COMMIT TRANSACTION;
  • 一般的 SQL 语句都是直接执行和编写的,即隐式提交(implicit commit),提交操作是自动进行的
  • 在事物处理中,提交通常通过 COMMIT 明确提交

20.2.3 使用保留点

SAVEPOINT delete1;
# DELTE ...
ROLLBACK TO delete1;
  • 保留点越多越好,以进行灵活地回退

20.3 小结

  • 事务是必须完整执行的 SQL 语句块
  • 使用 COMMIT 和 ROLLBACK 对写数据、撤销数据进行明确管理
  • 使用保留点控制回退操作
  • 参考具体 DBMS

第 21 课 使用游标

21.1 游标

  • 结果集(result set):SQL 查询所检索出的结果
  • 游标(cursor):一个存储在 DBMS 服务器上的数据库查询,不是一条 SQL 语句,而是被该语句检索出来的结果集。应用程序可以根据需要滚动或浏览其中的数据
  • 对于 WEB 应用用处不大,因为应用服务器是数据库客户端而不是最终用户

21.2 使用游标

21.3 小结


第 22 课 高级 SQL 特性

22.1 约束

  • 约束(constraint):管理如何插入或处理数据库数据的规则
  • DBMS 通过在数据库表上引用完整性(referential integrity)

22.1.1 主键

  • 主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,且永不改动
  • 任意列只要满足以下条件,都可以用于主键
    • 任意两行的主键值都不相同
    • 列中不允许 NULL 值
    • 列从不修改或更新
    • 主键值不能重用
  • 定义主键:创建表时或 CONSTRAINT PRIMARY KEY
CREATE TABLE test (
    name CHAR(10) NOT NULL PRIMARY KEY;
);
ALTER TABLE test 
ADD CONSTRAINT PRIMARY KEY (name);

22.1.2 外键

  • 外键:表中的一列,其值必须列在另一表的主键中。是保证引用完整性的极其重要部分
  • 定义外键:REFERENCES、CONSTRAINT FOREIGN KEY
  • 可以防止意外删除,DBMS 不允许删除在另一个表中具有关联行的行
CREATE TABLE module(
    author CHAR(20) NOT NULL REFERENCES user(name)
)
ALTER TABLE module
ADD CONSTRAINT FOREIGN KEY author REFERENCES user(name)

22.1.3 唯一约束

  • 唯一约束:用来保证一列(或一组列)中的数据是唯一的
  • 与主键的区别:
    • 表可以包含多个唯一约束
    • 唯一约束列可以包含 NULL
    • 唯一约束列可以修改或更新
    • 唯一约束列的值可以重复使用
    • 不能用来定义外键
  • 定义唯一约束:UNIQUE,用法同上

22.1.4 检查约束

  • 检查约束:用来保证一列(或一组列)中的数据满足一组指定的条件
  • 用途
    • 检查最小或最大值
    • 指定范围
    • 只允许特定的值
  • 定义检查约束:CHECK
  • 有的 DBMS 支持用户定义数据类型,即定义检查约束的基本简单数据类型
CREATE TABLE module(
    dist_size INT(10) CHECK (dist_size < 1024)
)
ADD CONSTRAINT CHECK (gender LIKE '[MF]')

22.2 索引

  • 索引:用来排序数据以加快搜索和排序操作的速度
  • 索引改善了检索操作的性能,但降低了数据插入、修改和删除的性能(DBMS 必须动态地更新索引)
  • 索引数据可能要占用大量的存储空间
  • 并非所有的数据都适合做缩影,具有更多可能值的数据能通过索引得到更多好处
  • 可以在索引中定义多个列,在组合多个列排序时有用
  • 索引的效率随着表数据的变化而变化,需要定期检查、调整索引
CREATE INDEX name_index ON user(name)

22.3 触发器

  • 触发器:特殊的存储过程,在特定的数据库互动发生时自动执行
  • 可以与特定表上的 INSERT、UDPATE、DELETE 操作(或组合)相关联
  • 用途
    • 保证数据一致,如在 INSERT 或 UPDATE 操作时将字段转化为大写
    • 基于某个表的变动在其他表上执行活动
    • 进行额外的验证并根据需要回退数据
    • 计算计算列的值或更新时间戳
  • 约束比触发器更快
  • 不同的 DBMS 差异大:CREATE TRIGGER ... FOR ... SET ...

22.4 数据库安全

  • 对数据库管理功能(创建表等)的访问
  • 对特定数据库或表的访问
  • 访问的类型(只读、对特定列的访问等)
  • 仅通过视图或存储过程对表进行访问
  • 创建多层次的安全措施,从而允许多种基于登录的访问和控制
  • 限制管理用户账号的能力

22.5 小结

  • 约束是实施引用完整性的重要部分
  • 索引可以改善数据检索的性能
  • 触发器可以用来执行运行前后的处理
  • 安全选项可以用来处理数据访问

附录 A 样例表脚本


附录 B 流行的应用程序


附录 C SQL 语句的语法


附录 D SQL 数据类型


附录 E SQL 保留字

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容