SQL 与关系型数据库直觉
SQL 用来描述想查询或修改什么,数据库负责安全高效地完成。PostgreSQL 与 MySQL 共享大部分核心概念。
- 能读懂常见 SQL,并知道危险修改前如何确认范围。
- SELECT、INSERT、UPDATE、DELETE
- 插入十条记录,完成筛选、排序、关联查询,并在事务中执行一次安全更新。
- 完成 6.1「为什么需要数据库」
FOUNDATION
必须理解
SQL 用来描述想查询或修改什么,数据库负责安全高效地完成。PostgreSQL 与 MySQL 共享大部分核心概念。
SQL(Structured Query Language,结构化查询语言)让你声明想读取或修改什么数据,由关系型数据库决定执行方式。理解表、行、列和关系后,ORM 生成的操作才可解释。
表像格式统一的登记册,行是一条记录,列是一项属性,主键是唯一档案号,外键是指向另一册档案的编号。查询像向档案馆提出条件。
users 表保存用户,tasks 表通过 owner_id 指向用户;SELECT 查询当前用户未完成任务,UPDATE 按 id 与 owner_id 修改状态。
SELECT、INSERT、UPDATE、DELETE
关系型数据库用表来组织结构,每一行是一条记录。主键像唯一档案号,外键则把这条记录和另一张表里的记录连起来。
基础概念
SQL(Structured Query Language,结构化查询语言)用声明式语句描述要读取或改变的数据。SELECT 查询,INSERT 新增,UPDATE 修改,DELETE 删除。
进一步理解
声明式表示你说明目标集合,数据库选择执行方式。列名、表名和条件共同决定范围,修改语句会真实改变持久数据。
执行 UPDATE 或 DELETE 前,先用相同 WHERE 做 SELECT,检查行数和具体记录;生产修改还应放入事务并准备回退。
先查询 `owner_id` 与任务 id 同时匹配的记录,再更新 done;返回影响行数为 0 时,说明不存在或不属于当前用户。
DELETE 与“从页面隐藏”不同,前者改变数据库;没有 WHERE 的 UPDATE/DELETE 可能作用整张表。
这一小节记住:任何修改语句都先确认目标集合,再执行并核对影响行数。
WHERE、ORDER BY、JOIN 与聚合
SELECT 负责查,INSERT 负责新增,UPDATE 用来改,DELETE 用来删。WHERE 决定到底作用在哪几行,忘记它可能一下改完整张表。
基础概念
WHERE 筛选行,ORDER BY 排序,JOIN 按关系组合多张表,聚合函数把多行计算成计数、总和或平均等结果。
进一步理解
执行顺序的直觉是先确定来源和连接,再筛选,随后分组聚合,最后选择输出与排序。实际优化由数据库负责。
JOIN 条件缺失会产生大量组合行;聚合与普通列混用通常需要 GROUP BY。NULL 代表缺失值,比较时有专门语义。
统计每个项目未完成任务数:连接 projects 与 tasks,筛选 done=false,按项目分组 count,并按数量降序。
JOIN 不是把两张表永久合并,查询结束后原表仍独立;ORDER BY 也不改变表内物理顺序。
这一小节记住:查询先确定想得到的行与列,再逐步加入连接、筛选、分组和排序。
约束、索引、事务和唯一性
索引像书的目录,找常用内容会快很多,但它也占空间并拖慢写入。事务则保证一组操作要么都成功,要么一起撤销,不留下半成品。
基础概念
约束在数据库层阻止非法数据,索引建立额外查找结构提升特定查询,事务把一组操作作为整体提交或撤销,唯一性保证目标值不重复。
进一步理解
主键、外键、NOT NULL、CHECK 与 UNIQUE 把关键规则靠近数据执行,即使多个入口写入也要遵守。
索引以存储空间和写入维护换读取速度;事务保证原子性,但并发隔离级别仍影响彼此可见范围。所有字段都加索引会拖慢写入。
创建任务与增加项目计数放进同一事务;ownerId 外键保证用户存在;常用的 ownerId 与 createdAt 组合查询再评估索引。
事务不是自动撤销业务错误,索引也不是查询越多越快。规则、查询计划和真实负载都需要验证。
这一小节记住:约束保护数据合法,事务保护操作完整,索引服务已证明的重要查询。
- 在测试数据库和事务中先用相同 WHERE 执行 SELECT。
- `$1` 与 `$2` 由数据库客户端作为参数绑定,不用字符串拼接。
UPDATE tasks
SET done = TRUE
WHERE id = $1 AND owner_id = $2
RETURNING id, title, done;同时限制任务 id 和当前用户 id,RETURNING 让调用方知道是否真的更新了记录。
- UPDATE 只修改 tasks 表中的 done 字段。
- WHERE 同时限制任务 id 与当前用户 id,形成资源归属边界。
- RETURNING 返回真正被更新的记录;空结果可映射为不存在或无权访问。
匹配 id 与 ownerId 时返回一条已完成任务;ownerId 不匹配时影响 0 行,其他用户的记录保持不变。
AI COLLABORATION
AI 如何参与
让 AI 生成 SQL 后解释影响行数、索引使用和事务边界;修改前先用 SELECT 验证目标。
推荐协作顺序
- 1
先用自然语言告诉 AI 你想查哪几行、改什么字段。
- 2
让它把 SQL 拆成 SELECT 验证和最终修改两步。
- 3
检查参数占位符与 WHERE 后,再在练习数据上执行。
请为 PostgreSQL 的 users 与 tasks 表写四条带解释的 SQL:创建一条任务、查询某用户未完成任务、按任务 id 和 owner_id 标记完成、删除一条属于该用户的任务。所有值使用参数占位符,不拼接用户输入。
每条修改语句都有明确条件,查询考虑用户归属,示例使用参数化而不是字符串拼接。
人工检查清单
- 重点检查 UPDATE/DELETE 的 WHERE、owner_id 权限边界和 SQL 注入风险。
- 让 AI 在每条修改 SQL 前给出等价 SELECT、预计影响行数与事务边界。
- 在测试数据上查看真实返回、约束错误和查询计划,不因语句语法正确就认为安全高效。
COMMON TRAPS
常见误区
错误不是需要隐藏的失败,而是帮助你看清系统边界的证据。下面三类问题在 AI 辅助学习中最常出现。
UPDATE 忘了 WHERE
- 你会看到
- 本来只想完成一条任务,结果整张表都变成已完成。
- 为什么发生
- 缺少 WHERE 的修改会把目标集合扩大到整表,数据库会准确执行这条危险指令。
- 怎样纠正
- 先用相同条件 SELECT,确认命中行数后再修改。
直接拼接用户输入
- 你会看到
- 把输入文字放进 SQL 字符串,留下 SQL 注入风险。
- 为什么发生
- 拼接用户输入会把数据变成 SQL 结构,参数化查询才能让数据库区分指令与值。
- 怎样纠正
- 使用参数化查询或 ORM 的安全查询接口。
看见慢查询就乱加索引
- 你会看到
- 索引越来越多,写入变慢,真正查询条件却没有分析。
- 为什么发生
- 索引数量越多,写入时需要维护的结构越多;没有查询证据的索引只会增加成本。
- 怎样纠正
- 先看实际查询与执行计划,再为高频条件设计索引。
HANDS-ON
动手任务
插入十条记录,完成筛选、排序、关联查询,并在事务中执行一次安全更新。
- 完成 6.1「为什么需要数据库」
跟着做
- 01
创建本地练习数据库与 users、tasks 表。
- 02
插入一名用户和三条任务。
- 03
用 SELECT 查询未完成任务并排序。
- 04
在事务中更新一条任务,先观察再提交。
- 05
故意使用不存在用户查询空结果,确认不是数据库错误。
能读懂常见 SQL,并知道危险修改前如何确认范围。
为 owner_id、done 组合查询思考索引,并用 EXPLAIN 观察计划,不要求立即优化。
离开本课前,自问四件事
- SELECT、INSERT、UPDATE 与 DELETE 分别改变什么?
- 执行 UPDATE 前怎样用 SELECT 验证目标范围?
- WHERE、JOIN、GROUP BY 和 ORDER BY 各解决什么问题?
- 约束、索引与事务为何不能互相替代?
确认完成后,会同步更新学习中心的课程学习进度。