Skip to content

第三章 SQL 语言详解


1. SQL 概述

结构化查询语言 (SQL, Structured Query Language) 是关系型数据库的标准语言。它不同于 C、Java 等过程式编程语言——SQL 是一种声明式 (Declarative) 语言,用户只需描述"需要什么样的数据",而无需指定"如何获取数据"的具体步骤。底层的查询路径选择由 DBMS 的查询优化器自动完成。

也正因为 SQL 是声明式语言,学习 SQL 时最容易出现两种偏差。第一种偏差是把它当成关键字清单,只记语法模板,不理解每个子句在结果语义中承担什么作用;第二种偏差是只追求“语句能跑出来”,却忽略结果口径、执行代价和协作可维护性。真正成熟的 SQL 能力,既包括把业务问题准确翻译成查询结构,也包括判断这条语句在真实系统中是否稳定、安全、可读。换句话说,SQL 既是一门语言,也是一种工程接口。

1.1 SQL 的标准化历程

SQL 经过多次标准化修订,主要版本包括:

  • SQL-86 / SQL-89:最初的工业标准,确立了 SELECT-FROM-WHERE 的基本框架。
  • SQL-92:目前兼容性最广泛的版本,引入了外连接语法(LEFT/RIGHT JOIN)、子查询等特性。
  • SQL:1999:引入了公用表表达式 (CTE)、递归查询、触发器标准和布尔类型。
  • SQL:2003:引入了窗口函数 (Window Functions),极大增强了分析查询能力。
  • 后续版本:陆续增加了对 JSON、XML、时序数据等的支持。

1.2 SQL 语言的功能分类

SQL 根据功能可分为以下几类:

  1. 数据定义语言 (DDL):用于定义和管理数据库对象的结构。
    • 主要语句:CREATEALTERDROPTRUNCATE
  2. 数据操纵语言 (DML):用于对表中的数据进行增删改查。
    • 主要语句:INSERTUPDATEDELETESELECT
  3. 数据控制语言 (DCL):用于权限管理。
    • 主要语句:GRANT(授权)、REVOKE(撤销权限)。
  4. 事务控制语言 (TCL):用于管理事务的提交和回滚。
    • 主要语句:COMMITROLLBACKSAVEPOINT

NOTE

SELECT 有时被单独称为数据查询语言 (DQL),但在广义的 DML 分类中通常将其包含在内。


2. 数据定义 (DDL)

2.1 创建表

使用 CREATE TABLE 语句定义表的结构,包括列名、数据类型和各种约束条件。

sql
CREATE TABLE Student (
    Sno    CHAR(9)    PRIMARY KEY,     -- 主键约束
    Sname  CHAR(20)   NOT NULL,        -- 非空约束
    Ssex   CHAR(2),
    Sage   SMALLINT   CHECK (Sage >= 0 AND Sage <= 150),  -- CHECK 约束
    Sdept  CHAR(20)
);

常见的列级约束包括:

  • PRIMARY KEY:主键约束,保证实体完整性。主键值不能为 NULL 且不能重复。
  • NOT NULL:非空约束。
  • UNIQUE:唯一性约束,允许存在一个 NULL 值(取决于具体 DBMS 实现)。
  • CHECK:检查约束,限定列值的合法范围。
  • DEFAULT:为列指定默认值。

表级约束可以在列定义之后统一声明,适用于联合主键或外键:

sql
CREATE TABLE SC (
    Sno    CHAR(9),
    Cno    CHAR(4),
    Grade  SMALLINT,
    PRIMARY KEY (Sno, Cno),                      -- 联合主键
    FOREIGN KEY (Sno) REFERENCES Student(Sno)     -- 外键约束
        ON DELETE CASCADE                         -- 级联删除
        ON UPDATE CASCADE                         -- 级联更新
);

建表语句之所以在 SQL 学习中格外重要,是因为它决定了后续所有增删改查的语义边界。很多初学者更喜欢先学查询,觉得建表只是“把字段列出来”,其实恰恰相反。表结构一旦设计粗糙,后面再复杂的查询也只能在模糊结构上打补丁。主键决定对象如何被唯一识别,数据类型决定值能以什么形式进入系统,约束决定哪些非法状态在入口就被挡住。数据库系统之所以可靠,首先不是因为查询写得漂亮,而是因为从第一条 CREATE TABLE 开始就把边界定义清楚了。

选择数据类型时,也不能只看“能不能存进去”,而要看它是否与业务语义一致。年龄字段用整数、金额字段用精确小数、状态字段用受限枚举或字典引用、时间字段明确时区语义,这些都属于设计阶段必须提前想清楚的问题。若图省事把大量字段都做成字符串,短期内可能觉得灵活,长期就会在排序错误、比较失真、索引失效和统计口径混乱中付出更高代价。SQL 教材之所以反复强调类型,不是形式主义,而是在帮助学习者建立“结构先于操作”的思维。

2.2 修改表结构

使用 ALTER TABLE 语句对已有的表结构进行调整:

  • 添加新列:ALTER TABLE Student ADD S_entrance DATE;
  • 修改列的数据类型:ALTER TABLE Student ALTER COLUMN Sage INT;
  • 添加约束:ALTER TABLE Student ADD CONSTRAINT ck_age CHECK (Sage >= 0);
  • 删除约束:ALTER TABLE Student DROP CONSTRAINT ck_age;

ALTER TABLE 之所以值得单独强调,是因为它代表数据库进入持续演进阶段。真实系统几乎不可能永远保持第一次建表时的结构不变,字段会新增,含义会扩展,约束会收紧,索引也会跟着访问模式调整。也正因为结构会变化,修改表结构时不能只盯着语法是否正确,还要考虑旧数据如何迁移、默认值如何补齐、是否会触发大表锁定、上层应用是否已经适配。数据库结构变更不是普通文本编辑,而是一次对线上数据契约的正式修改。

2.3 删除表

DROP TABLE Student; 会同时删除表的结构定义和所有数据,该操作不可回滚(因为它属于 DDL 操作)。

需区分三个相关操作:

  • DROPTABLE:删除表结构及数据。
  • DELETEFROMtable:删除表中的数据,表结构保留,操作可回滚,会触发触发器。
  • TRUNCATETABLE:清空表数据,表结构保留,不可回滚,速度比 DELETE 快,不触发行级触发器。

2.4 索引的创建与管理

索引是提升查询性能的核心手段。其作用在于通过建立额外的数据结构(如 B+ 树、哈希表),使 DBMS 无需逐行扫描整张表即可快速定位目标数据。

sql
CREATE INDEX idx_sname ON Student(Sname);          -- 普通索引
CREATE UNIQUE INDEX idx_sno ON Student(Sno);       -- 唯一索引
  • 单列索引与联合索引:联合索引的使用需遵循最左前缀匹配原则——查询条件必须从联合索引的最左列开始,才能有效利用该索引。
  • 索引的代价:虽然索引能加速查询,但每次 INSERTUPDATEDELETE 操作时都需要同步维护索引,因此不宜对频繁修改的列建立过多索引。

3. 数据操纵 (DML) 基础

3.1 插入数据 (INSERT)

3.2 更新数据 (UPDATE)

3.3 删除数据 (DELETE)

4. 数据查询 (SELECT) 详解

4.1 基本查询

4.2 条件查询 (WHERE)

4.3 聚合函数与分组查询 (GROUP BY, HAVING)

4.4 排序 (ORDER BY)

4.5 连接查询 (JOIN)

内连接 (INNER JOIN)

左外连接 (LEFT OUTER JOIN)

自连接 (Self Join)

4.6 子查询 (Subqueries)

标量子查询

使用 IN 的子查询

使用 EXISTS 的相关子查询

IN 与 EXISTS 的选择

4.7 集合操作

5. 视图 (Views)

5.1 视图的创建与使用

5.2 视图的作用

5.3 视图的更新限制

6. 窗口函数 (Window Functions)

6.1 基本语法

6.2 常用窗口函数

7. 公用表表达式 (CTE) 与递归查询

8. 数据库编程

8.1 存储过程 (Stored Procedures)

8.2 触发器 (Triggers)

8.3 游标 (Cursors)

8.4 嵌入式 SQL (Embedded SQL)

9. 数据控制 (DCL)

9.1 权限管理