附录
附录 A:SQL 语句执行顺序
写 SQL 时的书写顺序和数据库的实际执行顺序是不同的。理解这个差异能帮你写出更正确的 SQL,也能理解为什么某些写法会报错。
对照表
| 执行顺序 | 子句 | 做什么 |
|---|---|---|
| 1 | FROM / JOIN | 确定数据来源,执行表连接 |
| 2 | WHERE | 过滤行(在分组之前) |
| 3 | GROUP BY | 将行分组 |
| 4 | HAVING | 过滤分组(在分组之后) |
| 5 | SELECT | 选择列、计算表达式、去重(DISTINCT) |
| 6 | ORDER BY | 排序 |
| 7 | LIMIT / OFFSET | 限制返回行数 |
图示
书写顺序 执行顺序
SELECT ──────┐ ┌──── FROM / JOIN ①
FROM ──┐ │ │
JOIN ──┘ │ ├──── WHERE ②
WHERE ──────┤ │
GROUP BY ──────┤ ├──── GROUP BY ③
HAVING ──────┤ │
│ ├──── HAVING ④
└─────┤
├──── SELECT ⑤
ORDER BY ────────────┤
├──── ORDER BY ⑥
LIMIT ────────────┤
└──── LIMIT / OFFSET ⑦常见因执行顺序导致的错误
sql
-- 错误:WHERE 中不能用 SELECT 定义的别名(WHERE 先于 SELECT 执行)
SELECT name, age * 2 AS double_age FROM students
WHERE double_age > 40;
-- ERROR: column "double_age" does not exist
-- 正确写法
SELECT name, age * 2 AS double_age FROM students
WHERE age * 2 > 40;
-- 正确:ORDER BY 中可以用别名(ORDER BY 在 SELECT 之后执行)
SELECT name, age * 2 AS double_age FROM students
ORDER BY double_age DESC;附录 B:psql 完整快捷命令速查
信息查看
| 命令 | 作用 |
|---|---|
\l 或 \list | 列出所有数据库 |
\c dbname | 切换到指定数据库 |
\dt | 列出当前 schema 的所有表 |
\dt+ | 列出所有表(含大小信息) |
\d tablename | 查看表结构 |
\d+ tablename | 查看表详细结构(含存储信息) |
\di | 列出所有索引 |
\dv | 列出所有视图 |
\df | 列出所有函数 |
\du | 列出所有角色 / 用户 |
\dn | 列出所有 schema |
\ds | 列出所有序列 |
功能控制
| 命令 | 作用 |
|---|---|
\timing | 开启 / 关闭查询耗时显示 |
\x | 开启 / 关闭扩展显示模式(宽表变竖排) |
\a | 切换对齐 / 不对齐输出 |
\pset border 2 | 设置表格边框样式 |
文件与编辑
| 命令 | 作用 |
|---|---|
\i filename.sql | 执行 SQL 文件 |
\o filename.txt | 将输出重定向到文件 |
\e | 用外部编辑器写 SQL |
\s | 查看命令历史 |
系统操作
| 命令 | 作用 |
|---|---|
\! command | 执行系统命令(如 \! ls) |
\cd directory | 切换工作目录 |
\q | 退出 psql |
\? | 显示所有反斜杠命令的帮助 |
\h SQL命令 | 显示某个 SQL 命令的帮助(如 \h CREATE TABLE) |
附录 C:推荐学习路径
七步走完 PostgreSQL 入门
第一步 安装环境,用 psql 连上数据库 → 第 1 章
预计时间:30 分钟
目标:能执行 SELECT version(); 看到结果
第二步 建库建表,理解数据类型和约束 → 第 2 章
预计时间:45 分钟
目标:能独立建一张包含各种约束的表
第三步 增删改数据 → 第 3 章
预计时间:30 分钟
目标:熟练使用 INSERT/UPDATE/DELETE
第四步 SELECT 查询——本教程的核心 → 第 4 章
预计时间:2 小时
目标:掌握 WHERE/ORDER BY/GROUP BY/JOIN/子查询
第五步 理解索引和 EXPLAIN → 第 5 章
预计时间:30 分钟
目标:能用 EXPLAIN 判断查询是否走了索引
第六步 了解事务,能写 BEGIN/COMMIT → 第 6 章
预计时间:30 分钟
目标:理解 ACID,能手写事务
第七步 探索高级特性 + 基本运维 → 第 7~8 章
预计时间:1 小时
目标:知道 JSONB/窗口函数/CTE 的存在和用法总计约 5~6 小时,即可建立完整的 PostgreSQL 知识框架。
进阶方向
完成本教程后,可以根据自己的方向深入:
| 方向 | 建议学习内容 |
|---|---|
| 后端开发 | ORM(SQLAlchemy / Prisma)、连接池(PgBouncer)、数据库迁移 |
| 数据分析 | 窗口函数进阶、CTE 递归、物化视图、COPY 批量导入 |
| 数据库运维 | 性能调优(pg_stat_statements)、主从复制、备份策略、监控告警 |
| 全栈开发 | Supabase / Neon(云原生 PostgreSQL)、PostgREST(自动生成 REST API) |
推荐资源
| 资源 | 说明 |
|---|---|
| PostgreSQL 官方文档 | 最权威的参考手册 |
| PostgreSQL Tutorial | 英文入门教程,示例丰富 |
| Use The Index, Luke | 索引优化专题,非常推荐 |
| pgexercises.com | PostgreSQL 在线练习题 |
| Explain Visualizer | EXPLAIN 结果可视化工具 |
附录 D:本教程示例数据库完整建表脚本
以下脚本一次性创建本教程使用的所有表和示例数据,方便你从零开始练习。
sql
-- ============================================
-- PostgreSQL 入门教程 - 示例数据库
-- ============================================
-- 创建数据库
-- CREATE DATABASE school;
-- \c school
-- 学生表
CREATE TABLE students (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
age INTEGER CHECK (age BETWEEN 1 AND 150),
is_active BOOLEAN DEFAULT true,
enrolled_at TIMESTAMPTZ DEFAULT NOW()
);
-- 课程表
CREATE TABLE courses (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
student_id INTEGER REFERENCES students(id) ON DELETE CASCADE,
course_name VARCHAR(100) NOT NULL,
score NUMERIC(5,2)
);
-- 账户表(用于事务练习)
CREATE TABLE accounts (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(50) NOT NULL,
balance NUMERIC(10,2) NOT NULL CHECK (balance >= 0)
);
-- 事件表(用于 JSONB 练习)
CREATE TABLE events (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
event_type VARCHAR(50),
data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================
-- 插入示例数据
-- ============================================
INSERT INTO students (name, email, age) VALUES
('张三', 'zhangsan@example.com', 20),
('李四', 'lisi@example.com', 22),
('王五', 'wangwu@example.com', 21),
('赵六', 'zhaoliu@example.com', 23),
('孙七', 'sunqi@example.com', 20),
('周八', 'zhouba@example.com', 24),
('吴九', 'wujiu@example.com', 19),
('郑十', 'zhengshi@example.com', 22);
INSERT INTO courses (student_id, course_name, score) VALUES
(1, '数据库原理', 85.5),
(1, '操作系统', 90.0),
(1, '数据结构', 88.0),
(2, '数据库原理', 78.0),
(2, '数据结构', 92.5),
(3, '操作系统', 88.0),
(3, '数据结构', 76.0),
(4, '数据库原理', 95.0),
(4, '操作系统', 87.5),
(5, '操作系统', 82.5),
(5, '数据库原理', 79.0),
(6, '数据结构', 91.0),
(7, '数据库原理', 68.5),
(7, '操作系统', 72.0),
(8, '数据结构', 86.0),
(8, '数据库原理', 83.0);
INSERT INTO accounts (name, balance) VALUES
('小明', 1000.00),
('小红', 500.00),
('小李', 2000.00);
INSERT INTO events (event_type, data) VALUES
('click', '{"page": "/home", "button": "signup", "duration": 3.5}'),
('click', '{"page": "/pricing", "button": "buy", "duration": 1.2}'),
('pageview', '{"page": "/home", "referrer": "google.com"}'),
('pageview', '{"page": "/docs", "referrer": "github.com"}'),
('error', '{"code": 500, "message": "Internal Server Error", "stack": ["app.js:12", "router.js:45"]}');
-- ============================================
-- 创建常用索引
-- ============================================
CREATE INDEX idx_students_name ON students(name);
CREATE INDEX idx_courses_student_id ON courses(student_id);
CREATE INDEX idx_courses_name ON courses(course_name);
CREATE INDEX idx_events_data ON events USING GIN (data);
-- ============================================
-- 验证数据
-- ============================================
SELECT '学生数: ' || count(*) FROM students;
SELECT '选课数: ' || count(*) FROM courses;
SELECT '账户数: ' || count(*) FROM accounts;
SELECT '事件数: ' || count(*) FROM events;将以上脚本保存为 init_school.sql,然后执行:
bash
createdb -U postgres school
psql -U postgres -d school -f init_school.sql即可一键搭建完整的练习环境。
祝你学习顺利!建议配合实际操作练习,每章花 30~60 分钟动手敲 SQL,比纯看文档效果好 10 倍。