SQL 基础扫盲


读《SQL必知必会(第5版)》后记录

数据库基础

数据库

数据库(database)保存有组织的数据的容器(通常是一个文件或一组文件)

注意:数据库软件应称为数据库管理系统(DBMS),数据库是通过 DBMS 创建和操纵的容器

表

表(table)某种特定类型数据的结构化清单

列

表中的一个字段。所有表都是由一个或多个列组成的

行

表中的一个记录

主键

一列(或几列),其值能够唯一标识表中每一行

注释语法

  1. 使用 -- 注释内容

  2. 使用 # 注释内容(部分 DBMS 不支持)

  3. 使用 /* 注释内容 */

子句

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 的优点:

  1. IN 语法更清楚直观

  2. 在与其他 AND 和 OR 操作符组合使用 IN 时,求值顺序更容易管理

  3. IN操作符一般比一组OR操作符执行得更快

  4. 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 表名;

视图

视图本身是一个虚拟表,它的数据来自于表,通过执行时动态生成

好处:

  1. 简化 sql 语句

  2. 提高 sql 重用性

  3. 保护基表数据,提高安全性

-- 创建视图
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 游标名

文章作者: April-cl
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 April-cl !
  目录