接手没人设计过的客户数据库,先学哈佛这门免费 SQL 课
CS50 SQL 用七周讲透查询、表设计和扩展,练习用真实数据,从 SQLite 讲到 PostgreSQL。
要点速览
- CS50 SQL 在 OpenCourseWare 上免费开放,共七周,从第 0 周的查询讲到第 6 周的扩展。
- 对 FDE 来说,最有价值的是第 2 周:范式化、主键、外键和 ER 图。
- 课程先用 SQLite,再过渡到 PostgreSQL 和 MySQL,并讲解复制和预处理语句。
哈佛有一门只教一种语言的免费课程。CS50’s Introduction to Databases with SQL(通常简称 CS50 SQL)的课程主页写得很直白:这门课完全围绕 SQL。
不是哈佛学生也能学。所有讲座都放在 OpenCourseWare 上;如果需要证书,可以另外通过 edX 获取。
对想成为前线部署工程师(FDE)的人来说,这门课不是出于好奇随便看看的。客户现场的项目,往往始于一个不是你写的数据库,外加一份没人记得是谁建的电子表格。表设计错了,建在上面的一切,从仪表盘到 Agent,也都跟着错。
两位讲师,七周,一种语言
课程由 David J. Malan 和 Carter Zenke 讲授。Harvard Online 还列出 Zenke 是 Harvard Extension School 的 Preceptor。课程共七周,编号从第 0 周到第 6 周:查询(Querying)、关联(Relating)、设计(Designing)、写入(Writing)、视图(Viewing)、优化(Optimizing)和扩展(Scaling)。
这个顺序对学习者很合理。先要能读数据、理解表与表如何关联,才谈得上自己设计表、写入数据、自动化和优化。
课程先用 SQLite,因为它小巧、便于携带,到最后才引入 PostgreSQL 和 MySQL。练习用的是真实数据,而不是玩具表。
CS50 SQL 也不同于 CS50x。CS50x 广泛覆盖计算机科学,涉及 C、Python、SQL 和 JavaScript;CS50 SQL 只深挖 SQL。两门可以同时学,也可以只学 CS50 SQL。
一家公司算成两家:第 2 周的教训
最有价值的内容在第 2 周“设计”。课程教你把现实世界的实体和关系建模成表,选择合适的数据类型,并通过范式化消除重复、降低出错概率。讲义把这种拆分数据的做法称为 normalizing(范式化),并用 ER 图画出关系。
设想一家物流客户发来一份订单文件,每一行都记录了下单公司的名称。在 1,000 行数据里,同一个客户可能这里写成“ABC Company”,那里写成“ABC Co”。按客户统计营收的报表就会把一家公司拆成两家,任何查询都救不回来。
范式化正好解决这个问题。建一张 customers 表,以 id 为主键,orders 表只存 customer_id。第 2 周的讲座给出两条值得记住的约束:主键必须唯一;外键的值必须存在于关联表的主键列中。
有一步不能省:外键本身不会把“ABC Company”和“ABC Co”合并成一个。导入数据之前,仍然要清理名称的各种写法,在 customers 中合并成一行,再把每条已有订单映射到这个 id。
清理完成后,SQLite 中的最小版本大致如下:
PRAGMA foreign_keys = ON;
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
created_at TEXT,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
INSERT INTO customers (id, name) VALUES (1, 'ABC Company');
INSERT INTO orders (customer_id, created_at) VALUES (1, '2026-10-01'); -- 有效
INSERT INTO orders (customer_id, created_at) VALUES (99, '2026-10-01'); -- 被拒绝
第一条订单插入有效;最后一行指向不存在的 99 号客户,数据库会直接拒绝,而不是让错误流进报表。公司名称现在只存在一个地方,改一次,所有订单都随之更正。
开始一个新项目时,第一件事是画出客户数据库的 ER 图。如果发现某张表同时存着客户名称和订单明细,那就是写任何代码之前需要先问清楚的地方。
视图管业务逻辑,索引管速度
第 4、5 周“视图”和“优化”讲的两个工具,新手常常混淆。视图把反复运行的查询自动化;索引让查询跑得更快。
在客户现场,这个区别决定了你要做什么。运营团队每天都问“哪些订单延误超过三天?”,就写一个视图,让所有人复用同一个定义。同一个问题在大表上跑得慢,就需要索引。
视图让整个团队用同一个定义;索引只是让查询更快返回。课程还讲了如何把 SQL 接入 Python 和 Java,这正是应用代码在实际中与数据库协作的方式。
新手常犯的两个错误
第一个是用 SQLite 时忘了 PRAGMA foreign_keys = ON。不开启它,SQLite 不会强制执行外键,上面那条指向 99 号客户的 INSERT 就会悄无声息地通过。建议:每个连接都开启,然后故意插入一行坏数据,确认约束确实生效。
第二个是看到索引能加速查询,就给每一列都建索引。每次写入都要更新所有索引,还会占用额外存储。更稳妥的做法是只给慢查询的 WHERE 或 JOIN 子句中真正出现的列建索引,并在前后各测一次。
从 SQLite 到数据库服务器,以及 SQL 注入陷阱
第 6 周“扩展”点明了许多演示略过的一点:SQLite 是嵌入式数据库,而 MySQL 和 PostgreSQL 是数据库服务器。这一周的讲座还讲了复制,客户系统一旦用户多起来,你就会碰到它。
同样在第 6 周,课程讲解了如何在 MySQL 中用预处理语句防止 SQL 注入。对 FDE 来说,这是必备技能。如果你做的工具接收用户输入,并直接拼接进针对客户真实数据运行的 SQL,一条恶意输入就足以让整张表暴露。
不要拼接字符串,而是在语句中用 ? 作占位符,再把值传进去:
PREPARE find_orders FROM
'SELECT id, created_at FROM orders WHERE customer_id = ?';
SET @cid = 1;
EXECUTE find_orders USING @cid;
DEALLOCATE PREPARE find_orders;
@cid 的值只会被当作数据,永远不会成为语句的一部分。审查任何接触客户数据库的代码时,这是第一件要检查的事。
按什么顺序学,简历上写什么
如果你已经写了几年 SQL,可以快速浏览第 0、1 周,把大部分时间花在第 2 周和第 6 周。这两周最贴近客户现场的工作:设计表,以及把数据库带进真实环境。
如果你只用过 ORM,就按顺序学完七周,因为每一周都建立在前一周之上。
学完之后,把成果写进简历,而不只是写“会 SQL”。加一行说明你如何把一份真实数据集范式化、设了哪些约束、加了哪些索引,并附上代码仓库链接。
阅读 FDE 岗位描述时,如果看到“data modeling”或“customer data”这类字眼,就准备好一个范式化案例和一张自己画的 ER 图带去面试。这是证明你真正做过、而不只是学过的最具体的方式。
在客户现场,第一个问题通常不是“哪个模型最好”,而是“这张表能不能信”。CS50 SQL 能帮你在客户想到要问之前就给出答案。
这篇文章对你有帮助吗?
感谢你的反馈!