最近在整理数据库相关知识,发现 SQL 和关系代数这两个东西其实是同一枚硬币的两面。关系代数给你理论基础,SQL 给你实践工具。这篇文章就来聊聊这两个核心概念,算是我学习数据库的一份笔记。
什么是关系模型?
在聊 SQL 之前,我们先搞清楚关系模型(Relational Model)。这是 E.F. Codd 在 1970 年提出的,核心思想其实很简单:用表(Table)来表示数据。
一张表由几个关键部分组成:
- Relation(关系):就是一张表
- Tuple(元组):表中的一行,代表一条记录
- Attribute(属性):表中的一列,代表一个字段
- Schema(模式):表的结构定义,包括列名和数据类型
- Primary Key(主键):唯一标识一行的属性或属性组合
- Foreign Key(外键):引用其他表主键的属性,用于建立表之间的关联
举个例子,假设我们有一个学生选课系统,核心表结构如下:
Students (student_id PK, name, major, enrollment_year)
Courses (course_id PK, course_name, credits, department)
Enrollments (enrollment_id PK, student_id FK, course_id FK, grade, semester)
这里 Enrollments 就是一张关联表(Junction Table),通过外键把 Students 和 Courses 联系起来。这种设计体现了关系模型的精髓——数据之间通过外键约束建立联系,而不是把所有信息塞进一张大表。
关系代数:SQL 背后的数学
关系代数(Relational Algebra)是一套形式化的查询语言,是 SQL 的理论基础。它定义了对关系(表)进行操作的一组基本运算。理解关系代数,能帮你更好地理解 SQL 查询到底在干什么。
基本运算
关系代数有 6 种基本运算,其他运算都可以由它们组合而成:
1. 选择(Selection, σ)——按条件筛选行,对应 SQL 的 WHERE。
σ_{major=’CS’}(Students) 表示”从 Students 表中选出 CS 专业的学生”。SQL 里就是:
SELECT * FROM Students WHERE major = 'CS';
2. 投影(Projection, π)——选择特定的列,对应 SQL 的 SELECT 列名。
π_{name, major}(Students) 表示”只看 Students 的 name 和 major 列”:
SELECT name, major FROM Students;
3. 并(Union, ∪)——两个关系的合并,对应 SQL 的 UNION。
4. 差(Difference, −)——从一个关系中减去另一个关系中存在的元组,对应 SQL 的 EXCEPT。
5. 笛卡尔积(Cartesian Product, ×)——两个关系的所有可能组合,对应 SQL 的 CROSS JOIN。
6. 重命名(Rename, ρ)——给关系或属性换个名字,对应 SQL 的 AS。
派生运算
基于上面 6 个基本运算,我们可以构造出更强大的操作:
交(Intersection, ∩):两个关系的公共部分。R ∩ S = R − (R − S)。SQL 里直接用 INTERSECT。
连接(Join, ⋈):这可能是最常用的操作了。笛卡尔积 + 选择 = 连接。SQL 里的 JOIN 就是这个意思:
-- 自然连接(Natural Join):自动匹配同名属性
SELECT s.name, c.course_name, e.grade
FROM Students s
JOIN Enrollments e ON s.student_id = e.student_id
JOIN Courses c ON e.course_id = c.course_id;
除(Division, ÷):这个稍微抽象一点。它回答的是”哪些 X 与所有 Y 都有关联”这类问题。比如”选了所有 CS 课程的学生”:
-- 用 SQL 实现除法(没有直接的 DIVIDE 运算符)
SELECT DISTINCT s.name
FROM Students s
WHERE NOT EXISTS (
SELECT c.course_id FROM Courses c
WHERE c.department = 'CS'
AND NOT EXISTS (
SELECT e.enrollment_id FROM Enrollments e
WHERE e.student_id = s.student_id
AND e.course_id = c.course_id
)
);
💡 Tips:关系代数的除法在 SQL 中没有直接的运算符,通常用双重 NOT EXISTS 来实现。这是面试中常见的考点!
SQL CRUD:日常操作四件套
了解了关系代数,我们来看看 SQL 的实际操作。CRUD 是 Create、Read、Update、Delete 的缩写,基本上你和数据库打交道 90% 的时间都在做这四件事。
Create —— 插入数据
-- 插入单条记录
INSERT INTO Students (student_id, name, major, enrollment_year)
VALUES (1, 'Alice', 'CS', 2023);
-- 批量插入
INSERT INTO Students (student_id, name, major, enrollment_year)
VALUES
(2, 'Bob', 'Math', 2023),
(3, 'Charlie', 'CS', 2024),
(4, 'Diana', 'Physics', 2022);
Read —— 查询数据
查询是 SQL 中最复杂的部分,也是关系代数的核心应用。来几个常用的例子:
-- 基础查询 + 条件筛选(σ 选择)
SELECT name, major FROM Students
WHERE enrollment_year >= 2023
ORDER BY name ASC;
-- 聚合查询
SELECT major, COUNT(*) AS student_count, AVG(enrollment_year) AS avg_year
FROM Students
GROUP BY major
HAVING COUNT(*) > 1;
-- 子查询:找出选了 'Database Systems' 这门课的学生
SELECT name FROM Students
WHERE student_id IN (
SELECT student_id FROM Enrollments
WHERE course_id = (
SELECT course_id FROM Courses
WHERE course_name = 'Database Systems'
)
);
Update & Delete
-- 更新:给 Alice 改专业
UPDATE Students SET major = 'Data Science'
WHERE student_id = 1;
-- 删除:移除成绩为 F 的选课记录
DELETE FROM Enrollments
WHERE grade = 'F';
⚠️ 警告:UPDATE 和 DELETE 不带 WHERE 条件的话会影响全表!生产环境一定要小心,建议先用 SELECT 确认影响范围。
JOIN 深入:连接的几种类型
JOIN 是 SQL 中最重要的操作之一,对应关系代数中的连接运算。常见的几种类型:
-- INNER JOIN:只返回两边都匹配的行
SELECT s.name, e.grade
FROM Students s
INNER JOIN Enrollments e ON s.student_id = e.student_id;
-- LEFT JOIN:左表全部保留,右表没有匹配的填 NULL
SELECT s.name, e.grade
FROM Students s
LEFT JOIN Enrollments e ON s.student_id = e.student_id;
-- 自连接:和自己连,比如找同专业的学生对
SELECT a.name AS student1, b.name AS student2
FROM Students a
JOIN Students b ON a.major = b.major AND a.student_id < b.student_id;
理解 JOIN 的关键在于想象两个集合的韦恩图(Venn Diagram):INNER JOIN 取交集,LEFT JOIN 取左集全集加交集,RIGHT JOIN 反之,FULL OUTER JOIN 取全集。
关系代数与 SQL 的对应关系
最后来个总结,把关系代数和 SQL 的对应关系理清楚:
- σ 选择 → WHERE 子句
- π 投影 → SELECT 列名
- ∪ 并 → UNION
- − 差 → EXCEPT
- × 笛卡尔积 → CROSS JOIN
- ⋈ 连接 → JOIN (INNER/LEFT/RIGHT)
- ÷ 除法 → 双重 NOT EXISTS
- ρ 重命名 → AS
搞清楚这个映射关系,你就能从理论层面理解 SQL 查询在做什么,写复杂查询的时候也会更有思路。
一些实用技巧
最后分享几个实战中的小技巧:
- EXPLAIN:用 EXPLAIN 查看查询执行计划,了解数据库如何优化你的查询
- Index:在经常用于 WHERE、JOIN、ORDER BY 的列上建索引,能显著提升查询速度
- 避免 SELECT *:只选需要的列,减少数据传输量
- CTE(Common Table Expression):用 WITH 子句把复杂查询拆分成可读的步骤
- 事务(Transaction):多条相关操作用 BEGIN/COMMIT/ROLLBACK 包裹,保证数据一致性
-- CTE 示例:找出每个专业成绩最好的学生
WITH RankedStudents AS (
SELECT s.name, s.major, e.grade,
RANK() OVER (PARTITION BY s.major ORDER BY e.grade DESC) AS rnk
FROM Students s
JOIN Enrollments e ON s.student_id = e.student_id
)
SELECT name, major, grade FROM RankedStudents WHERE rnk = 1;
好了,这篇笔记就到这里。SQL 和关系代数是数据库的基石,掌握了它们,后面学习索引优化、事务处理、分布式数据库等内容就有了扎实的基础。如果你也在学数据库,希望这篇文章对你有帮助!