SQL¶
约 2707 个字 157 行代码 预计阅读时间 15 分钟
一定要记住查询语句的执行顺序,才能理清逻辑
Overview¶
SQL(Sequel)是一个语言规范,最初由 IBM 设计。不过很多 DBMS 并不会完全按照规范设计它们的语言,每个人都想要将它们的 feature 加入 SQL 后续的更新中。
我不想我的 SQL 语句能被别的厂商直接使用()
按照标准符合度(Conformance Levels),可以分为四类:
- Entry Level SQL 入门级
- Transitional SQL 过渡级
- Intermediate SQL 中间级
- Full SQL 完全级
到了现在,已经没有 DBMS 产品会实现 FULL LEVEL 的标准符合度了。
作为一个关系型语言,SQL 主要由以下三个方面组合而成:
- Data Manipulation Language (DML)
- 数据操作语言,即检索数据或修改数据的命令
select ... from ...
insert
,delete
,update
- Data Defination Language (DDL)
- 数据定义语言,可涉及到索引、命名空间、触发器、函数等,通常为数据库声明元数据
create table
,alter table
,drop table
create index
,drop index
create view
,drop view
create trigger
,drop trigger
- Data Control Language (DCL)
- 数据控制语言,关于安全和类似内容的规范
grant
,revoke
Data Definition Language¶
一个典型的 DDL 语句如下:
SQL 的 Domain Type 大体有以下几类:
char(n)
: 用户指定的定长字符串- C语言中字符串一定以 0 结尾,但是 DB 中并不需要
varchar(n)
: 用户指定最大长度的可变长度字符串int
: 整数,根据机器的不同指定的范围不同smallint
: 整数,比int
范围更小numeric(p,d)
: Fixed Point Number,p 表示有效数字位数,d 表示小数点后多少位- 例如,
numeric(3,1)
允许 44.5,但不允许 444.5 或者 0.32
- 例如,
real, double
: 浮点数,根据机器的不同精度不同float(n)
: 浮点数,由用户指定最小精度null values
: 空date
: 包含年(4 digit)、月、日- E.g.
2007-2-27
- E.g.
time
: Time of day, in hours, minutes, and seconds- E.g.
11:18:16
or11:18:16.28
- E.g.
timestamp
: 时间戳,通常是 Data + Time- E.g.
2007-2-27 11:18:16.28
- E.g.
- Large Object Types,用来存储大文件,如
blob(20MB)
blob
: Binary Large Objectclob
: Character Large Object
SQL 有许多函数用来处理各种类型的数据及其类型转换,但各数据库系统中函数的标准化程度不高
除了在 CREATE
语句中指定 Domain Type 外,还需要加入必要的 Integrity Constraints 来确保数据库的完整性:
not null
: 要求非空default
: 设定缺省值check(p)
: 检测谓词 p 是否成立Primary Key(A1, A2,...)
: 括号内不能为空、不能有重复 KeysForeign Key(A1, A2,...) references r
: 引用对应表的主键
Referential Integrity 的检测只在一个事务结束时进行,因此事务中间可以暂时违反
SQL 还支持用户自定义 Types 或 Domain:
区别在于,DOMAIN
允许添加约束
不要轻易使用 DROP 指令,DELETE 起码还会把 SCHEMA 留下来,DROP 了什么都没了
在不读取整个表的情况下,索引使得数据库应用程序可以更快查找数据,并且是用户不可见的。
由于索引本身也需要更新,更新一个带索引的表要比没有索引的同一表消耗更多时间,因此,理想的做法是只在常常被搜索的 Column 上建立索引。
To Drop an index
Basic Structure¶
等价于逻辑代数:
SQL 允许 Relation 中出现重复元组,使用 distinct
关键字来强制筛选
在 SQL Server 中,可以使用等于号来代替上式的 as
SQL 还提供 String-Matching Operator,通常在 where
语句中使用 LIKE
运算符:
%
: 匹配任意字串_
: 匹配任意单字符
Set Operations¶
在 SQL 中,集合操作有 UNION
, INTERSECT
, EXCEPT
,分别对应着并集 \(\cup\),交集 \(\cap\),以及差集 \(-\)。与其他操作不同的是,set operations 会自动隐去重复的元素,如果我们想保留重复元组,则需使用 UNION ALL
等关键字。
Suppose a tuple occurs m times in r and n times in s, then it occurs:
- \(m+n\) times in
r UNION ALL s
- \(\min (m,n)\) times in
r INTERSECT ALL s
- \(\max (0, m-n)\) times in
r EXCEPT ALL s
Example. Find all customers who have an account but no loan.
注意不同 DBMS 之间差别,例如 Oracle 使用 MINUS
代替 EXCEPT
,SQL Server 2000 只支持 UNION
和 UNION ALL
等
Aggregate Functions¶
带聚合函数的 SELECT
之后只能跟着 GROUP BY
子句中的属性或者聚合函数
Example. Find the average account balance for each branch
由于 WHERE
的计算顺序比聚合函数前,所有当执行 WHERE
判断时聚合函数还没有得到结果,因此我们不能在 WHERE
中加入聚合函数。相对的,我们使用 HAVING
关键字来作为代替:
Example. Find the names of all branches located in city Brooklyn where the average account balance is more than $1,200
除了 COUNT(*)
的聚合函数默认忽略 Null Values,除非该值全部为 null
,此时也会返回 null
。
Null Values
任意带 null
的算术计算结果都是 null
,任意带 null
的逻辑比较结果都是 unknown
。此外,三种带 unknown
的逻辑计算结果为:
同时,作为 WHERE
子句中的谓词,需要使用 WHERE amount is not null
的形式来判断 null values,而不能直接用等于号。WHERE
最终结果如果为 unknown
,也当作 false
处理。
关键字 rollup
group by rollup(A,B,C)
能够自动生成多级别的汇总数据:
- 按 (A, B, C) 分组,计算最详细的分组统计。
- 按 (A, B) 分组,忽略 C,计算 B 级别的汇总。
- 按 (A) 分组,忽略 B 和 C,计算 A 级别的汇总。
- 整体汇总,不按任何列分组,计算全局统计。
Nested Query¶
我们知道 FROM
甚至 WHERE
子句中都可以以一张表作为对象,而我们 SELECT
语句得到的结果就是一张表,因此我们完全可以进行嵌套查询,下面是一个简单例子:
那么,如果我们想查找各个 branch 中 balance 最大的账户,该如何构造嵌套查询?
分析语序,此处相当于一个两层嵌套循环,先从外层 A
取出一个 branch_name
,然后找出内层所有相同 branch_name
的元组,计算最大值,再回到外层进行比较。这样唯一符合的结果就是该 branch_name
中 balance 最大的账户了。
除此之外,SQL 还有一种 set comparison,分别是 SOME
和 ALL
关键词:
VIEW¶
VIEW 并不占用实际空间,是一张更方便查看的逻辑表,同时也可以被当作正常表来访问(以及嵌套查询)。
下面的 SQL Server 语句创建了一个用于统计各个用户的贴数的 VIEW:
使用 DROP VIEW <V_NAME>
来删除 VIEW
我们甚至可以对 VIEW 进行 Modification,DBMS 会尽可能将其“翻译”为对基表的操作。但我们尽量不要这么做。只有行列视图可以更新数据。
行列视图
建立在单个基本表上的视图,且视图的列对应表的列,称为行列视图。
通过 WITH
关键字,我们可以建立一个 LOCAL VIEW,使得嵌套查询变得更加美观:
Modification¶
Modification 即我们熟识的增删改 INSERT
, DELETE
, UPDATE
:
Join Operation¶
连接操作是一个二元运算符,可以分为自然连接和非自然连接两类,区别在于 Natural 自动以同名属性相等作为连接条件,而非自然连接可以使用 ON
或USING
关键字自定义条件:
外连接(非 INNER JOIN
)如果找不到对应的连接对应关系,也要加入到结果中:
Example. Find all customers who have either an account or a loan (but not both) at the bank
找到只拥有 account 或 loan 的 customers。
Assertions & Triggers¶
assertion 也是确保数据库 integrity 的方式。当我们设置一个 assertion 后,DBMS 会在每一次更新后检测:
如果 CHECK
不满足,则会拒绝这次更改。
Triggers 则是 DBMS 在某些特定的 Modification 后自动执行的一串语句。下面是一个简单的 Trigger 例子,其在检测到 balance 为负数时触发:
相当于自动将这个 account 转换为 loan 贷款。
上述例子中,AFTER update
, new row
, for each row
等都是 Trigger 相关的关键字,除此之外还有 BEFORE delete
, old row
, for each statement
等。
Authorization¶
- <1> Read Authorization - allows reading, but not modification of data.
- <2> Insert Authorization - allows insertion of new data, but not modification of existing data.
- <3> Update Authorization - allows modification, but not deletion of data.
- <4> Delete Authorization - allows deletion of data.
四种权限按顺序由浅入深。
对于数据库 Schema 的更改,有四个相关权限
- Index Authorization - allows creation and deletion of indices.
- Resources Authorization - allows creation of new relations.
- 创建
VIEW
并不需要 Resources Authorization
- 创建
- Alteration Authorization - allows addition or modifying of attributes in a relation.
- Drop Authorization - allows deletion of relations.
VIEW
是提高数据库安全性的一种策略,它可以只提供给用户他们需要的数据,例如我们想要对用户隐藏 loan_number
:
VIEW
的建立者对该 VIEW
的权限也仅限于他本身有的权限。例如,他对 borrower
, loan
两个表都只有 Read Authorization ,那么他对 cust_loan
也只有 Read Authorization。
Authorization 检测一定要在 Query Processor 将 VIEW 替换为实际的表前进行
Authorization 可以通过 GRANT
指令传递,一个典型的 Authorization Graph 如下:
graph TD;
DBA[DBA] -->|grants| User1[User 1]
DBA -->|grants| User2[User 2]
DBA -->|grants| User3[User 3]
User1 -->|grants| User4[User 4]
User1 -->|grants| User5[User 5]
User2 -->|grants| User5
Authorization Graph 一定满足任何节点都可以追溯到 DBA,否则将该 Edge 删除。例如,如果 DBA Revoke User 1,那么 User 4 此时也失去权限;而 User 5 则还有 User 2 赋予的权限。
父节点的权限一定大于等于子节点
SQL 中 GRANT
和 REVOKE
语句一般格式如下:
其中 <privilige list>
可选的选项为:
- <1> insert
- <2> update
- <3> delete
- <4> references
- 创建 Relation 时声明 Foreign Keys 的权限
- <5> all priviliges/all
- 所有权限
<user list>
可选的选项为:
- <1> user-ids
- <2> public
- 表示允许所有 Valid Users
- <3> Role
ROLE
Role 为一组 User 提供他们共有的 Privilege
由于在数据库层面(tuple level)实现 Authorization 控制开销过大,因此我们在应用层上实现。
Tuple Level 指只允许访问某几行
Audit Trail 是记录了所有更改的 Log,其包含哪个用户在哪个时间进行了什么更改等信息。具体可以用 TRIGGER
实现,但是很多 DBMS 也直接支持这一特性:
取消审计用 NOAUDIT
语句
审计结果一般记录在数据字典表 sys.aud$
中,也可以从 dba_audit_trail
、dba_audit_statement
、dba_audit_object
等表中获得有关情况。上述表仅有 DBA 可见。