读《SQL必知必会(第5版)》后记录
数据库基础
数据库
数据库(database)保存有组织的数据的容器(通常是一个文件或一组文件)
注意:数据库软件应称为数据库管理系统(DBMS),数据库是通过 DBMS 创建和操纵的容器
表
表(table)某种特定类型数据的结构化清单
列
表中的一个字段。所有表都是由一个或多个列组成的
行
表中的一个记录
主键
一列(或几列),其值能够唯一标识表中每一行
注释语法
使用
-- 注释内容
使用
# 注释内容
(部分 DBMS 不支持)使用
/* 注释内容 */
子句
SQL语句由子句构成,有些子句是必需的,有些则是可选的。一个子句通常由一个关键字加上所提供的数据组成
查
检索单列
SELECT 列名 FROM 表名;
检索多列
SELECT 列名1, 列名2 FROM 表名;
SELECT * FROM 表名;
检索不同的值
SELECT DISTINCT 列名 FROM 表名;
限制结果
返回指定表中所有匹配的行,各种数据库中的这一 SQL 实现并不相同,示例用 MySQL,下同
SELECT 列名 FROM 表名 LIMIT 行数;
SELECT 列名 FROM 表名 LIMIT 行数 OFFSET 偏移行数;
注意:第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1会检索第2行,而不是第1行。
排序
SELECT 列名 FROM 表名 ORDER BY 列名(或列别名);
SELECT 列名 FROM 表名 ORDER BY 列名(或列别名) ASC; -- 升序,默认
SELECT 列名 FROM 表名 ORDER BY 列名(或列别名) DESC; -- 降序
注意:在指定一条 ORDER BY 子句时,应该保证它是 SELECT 语句中最后一条子句
提示:ORDER BY 可以用非检索的列排序数据(即 ORDER BY 后的列不一定是在 SELECT 后出现的列)
过滤
SELECT 列名 FROM 表名 WHERE 条件
条件操作符
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
!< | 不小于 |
> | 大于 |
>= | 大于等于 |
!> | 不大于 |
BETWEEN | 在指定的两个值之间 |
IS NULL | 为 NULL 值 |
逻辑操作符
操作符 | 说明 |
---|---|
AND | 检索满足所有给定条件的行 |
OR | 检索匹配任一给定条件的行 |
IN | 用来指定条件范围,范围中的每个条件都可以进行匹配 |
NOT | 否定其后所跟的任何条件 |
IN 相对 OR 的优点:
IN 语法更清楚直观
在与其他 AND 和 OR 操作符组合使用 IN 时,求值顺序更容易管理
IN操作符一般比一组OR操作符执行得更快
IN 可以包含其他 SELECT 语句
注意:SQL 在处理 OR 操作符前,优先处理 AND 操作符,为避免歧义,应该使用圆括号明确地分组操作
通配符
通配符 | 说明 |
---|---|
% | 表示任何字符出现任意次数 |
_ | 只匹配单个字符 |
[] | 指定一个字符集 |
在搜索子句中使用通配符,必须使用 LIKE 操作符
计算字段
计算字段是运行时在SELECT语句内创建的,并不实际存在于数据库表中
计算字段使用例子:拼接字段、执行算术计算
拼接字段:将值联结到一起构成单个值,根据使用的 DBMS 选择用 + 或 || 拼接
函数
只有少数几个函数被所有主要的 DBMS 等同地支持,即 SQL 函数不是可移植的
聚集函数
对某些行运行的函数,计算并返回一个值
函数 | 说明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
以上5个聚集函数都可以如下使用
对所有行执行计算,指定 ALL 参数或不指定参数(因为ALL是默认行为)
只包含不同的值,指定 DISTINCT 参数
示例
-- 平均值只考虑各个不同的价格
SELECT AVG(DISTINCT prod_price) AS avg_price FROM Products;
注意:DISTINCT 能用于 COUNT(*)
如果指定列名,则 DISTINCT 只能用于 COUNT()。DISTINCT 不能用于 COUNT(*)。类似地,DISTINCT 必须使用列名,不能用于计算或表达式
数据分组
SELECT 列名 FROM 表名 GROUP BY 列名;
SELECT 列名 FROM 表名 GROUP BY 列名 HAVING 条件;
一些重要的规定:
GROUP BY 子句可以包含任意数目的列,因而可以对分组进行嵌套,更细致地进行数据分组
如果在 GROUP BY 子句中嵌套了分组,数据将在最后指定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以不能从个别的列取回数据)
GROUP BY 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在 SELECT 中使用表达式,则必须在 GROUP BY 子句中指定相同的表达式。不能使用别名
大多数SQL实现不允许 GROUP BY 列带有长度可变的数据类型(如文本或备注型字段)
除聚集计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句中给出
如果分组列中包含具有 NULL 值的行,则 NULL 将作为一个分组返回。如果列中有多行 NULL 值,它们将分为一组
GROUP BY 子句必须出现在 WHERE 子句之后,ORDER BY 子句之前
SELECT 子句顺序
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
子查询
嵌套在其他查询中的查询
子查询使用例子:
-- 利用子查询进行过滤
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
-- 作为计算字段使用子查询
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
联结
叉联结
笛卡儿积:由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数
返回笛卡儿积的联结,也称叉联结(cross join)
内联结
从数学的角度就是求两个表的交集
SELECT 列名 FROM 表1 INNER JOIN 表2 ON 条件;
自联结
针对相同的表进行的联结
自然联结
实话实说看书没看懂,看这篇看懂了
外联结
-- 左联结
SELECT 列名 FROM 表1 LEFT OUTER JOIN 表2 ON 条件;
-- 右联结
SELECT 列名 FROM 表1 RIGHT OUTER JOIN 表2 ON 条件;
-- 全联结,部分 DBMS 不支持
SELECT 列名 FROM 表1 FULL OUTER JOIN 表2 ON 条件;
带聚集函数的联结
示例:检索所有顾客及每个顾客所下的订单数
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers
INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
注意区别前面数据分组提到的规定:
- 除聚集计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句中给出
组合查询
SELECT 语句1
UNION
SELECT 语句2
ORDER BY 列名
UNION 进行组合时需要注意几条规则:
UNION 必须由两条或两条以上的 SELECT 语句组成,语句之间用关键字 UNION 分隔
UNION 中的每个查询必须包含相同的列、表达式或聚集函数(不过,各个列不需要以相同的次序列出)
列数据类型必须兼容:类型不必完全相同,但必须是 DBMS 可以隐含转换的类型(例如,不同的数值类型或不同的日期类型
UNION:从查询结果集中自动去除了重复的行
UNION ALL:返回所有的匹配行
增
-- 插入自定义的行
INSERT INTO 表名 VALUS(值); -- 不指定列名,需按表定义中出现的次序填充
INSERT INTO 表名(列) VALUS(值); -- 指定列名
-- 插入检索出的数据
INSERT INTO 表名(列) SELECT 语句;
-- 将一个表的内容复制到一个全新的表
CREATE TABLE 表1 AS SELECT * FROM 表2; -- MySQL 支持的写法,需要看具体使用的 DBMS
需要客户端/服务器 DBMS 中的特定安全权限
改
UPDATE 表名 SET 列1 = 值1, 列2 = 值2 WHERE 条件;
需要客户端/服务器 DBMS 中的特定安全权限
删
DElETE FROM 表名 WHERE 条件;
DELETE 删除整行而不是删除列。要删除指定的列,请使用 UPDATE 语句(UPDATE 表名 SET 列 = NULL WHERE 条件;
)
需要客户端/服务器 DBMS 中的特定安全权限
注意
除非确实是需要更新/修改每一行,否则使用 UPDATE 或 DELETE 语句一定要带上 WHERE 子句
在 UPDATE 或 DELETE 语句使用 WHERE 子句前,应该先用 SELECT 进行测试,保证它过滤的是正确的记录,以防编写的 WHERE 子句不正确
表操作
创建表
一般有两种创建表的方法:
多数 DBMS 都具有交互式创建和管理数据库表的工具
表也可以直接用 SQL 语句操纵(在不同的 SQL 实现中,CREATE TABLE 语句的语法可能有所不同)
利用 CREATE TABLE 创建表,必须给出下列信息:
新表的名字,在关键字 CREATE TABLE 之后给出
表列的名字和定义,用逗号分隔
有的DBMS还要求指定表的位置
示例
CREATE TABLE OrderItems
(
order_num INTEGER NOT NULL,
order_item INTEGER NOT NULL,
prod_id CHAR(10) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
item_price DECIMAL(8,2) NOT NULL
);
NULL 值就是没有值或缺值。允许 NULL 值的列也允许在插入行时不给出该列的值,不允许 NULL 值的列不接受没有列值的行
默认值在 CREATE TABLE 语句的列定义中用关键字 DEFAULT 指定
更新表
使用 ALTER TABLE 更改表结构,必须给出下面的信息:
在ALTER TABLE 之后给出要更改的表名(该表必须存在,否则将出错)
列出要做哪些更改
-- 增加列
ALTER TABLE 表名 ADD 列 数据类型...;
-- 删除列
ALTER TABLE 表名 DROP COLUMN 列;
删除表
DROP TABLE 表名;
视图
视图本身是一个虚拟表,它的数据来自于表,通过执行时动态生成
好处:
简化 sql 语句
提高 sql 重用性
保护基表数据,提高安全性
-- 创建视图
CREATE VIEW 视图名 AS 查询语句;
-- 删除视图
DROP VIEW 视图名;
存储过程
存储过程是为了完成特定功能的 SQL 语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数来调用执行(理解为函数?)
不同 DBMS 中的存储过程语法有所不同。事实上,编写真正的可移植存储过程几乎是不可能的。不过,存储过程的自我调用(名字以及数据如何传递)可以相对保持可移植
-- 创建存储过程
CREATE PROCEDURE 存储过程名(参数模式 参数名 参数类型)
BEGIN
存储过程体
END
-- 调用存储过程
CALL 存储过程名(实参)
参数模式: IN | OUT | INOUT,IN 可以省略
事务
事务处理是一种机制,用来管理必须成批执行的 SQL 操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态
事务处理用来管理 INSERT、UPDATE 和 DELETE 语句。不能回退 SELECT 语句(回退 SELECT 语句也没有必要),也不能回退 CREATE 或 DROP 操作。事务处理中可以使用这些语句,但进行回退时,这些操作也不撤销。
-- 开始事务
START TRANSACTION;
-- 事务回滚
ROLLBACK;
-- 提交事务
COMMIT;
-- 用于在事务中设置保存点,以便稍后能够回滚到该点
SAVEPOINT 保存点;
-- 用于回滚到之前设置的保存点
ROLLBACK TO SAVEPOINT 保存点;
游标
-- 创建游标
DECLARE 游标名 CURSOR FOR 查询语句;
-- 打开游标
OPEN CURSOR 游标名
-- 访问游标数据
FETCH 游标名 INTO 变量;
-- 关闭游标
CLOSE 游标名