往期链接:
SQL学习文档-CSDN博客
关系数据库标准语言(SQL)- 软考备战(三十一)-CSDN博客
数据库系统 – 汇总篇-CSDN博客
数据库事务与并发控制 – 软考备战(三十二)-CSDN博客
分布式数据库 – 软考备战(三十六)-CSDN博客
数据库基础概念与体系结构 – 软考备战(二十九)-CSDN博客
数据库安全性与完整性 – 软考备战(三十三)-CSDN博客
数据库设计 – 软考备战(五十一)-CSDN博客

第 00 章 · 序言与知识地图
0.1 2026 年的 SQL 行业格局
SQL 核心语法几十年相对稳定,但「合格线」持续上移,行业格局出现明确分化:
窗口函数、CTE 已成为基础能力:数据分析、数仓开发、后端开发岗位均默认要求掌握,不再是「高级选修」。
引擎生态分化明确:
- OLTP 领域:PostgreSQL 凭借 JSONB、pgvector 向量扩展、高度的标准兼容性,成为新项目的默认选择,2026 年开发者渗透率突破 55%,新增项目选型比例达 MySQL 的 3 倍;MySQL 仍在存量 Web 生态占据主流。
- 本地分析领域:DuckDB 成为「嵌入式数仓」事实标准,可直接查询本地文件,开发效率与性能双优,是本地数据分析、单元测试、边缘计算的首选。
- 云数仓:Snowflake、BigQuery、Redshift 持续主导云端大规模分析场景,扫描成本、分区裁剪是核心优化点。
- HTAP 与分布式:TiDB、OceanBase、PolarDB 等国产分布式数据库快速崛起,兼顾事务与分析能力,信创场景应用广泛。
- 实时分析:ClickHouse、StarRocks 主导 PB 级实时 OLAP 场景。
标准与落地:最新完整版标准仍是 SQL:2023,原生 JSON、属性图查询 SQL/PGQ、多维聚合等特性持续落地,但各引擎进度差异较大。
工作模式变革:AI 辅助写 SQL 全面普及,72% 的数据团队已将 AI 编码融入日常流程;验结果、控质量、懂逻辑 比「手写 SQL 速度」更重要。
工程化成熟:分析工程成为独立岗位方向,dbt 等工具将 SQL 从「查询语句」升级为「可测试、可复用、可追溯的数据管道」。
0.2 分岗位学习侧重点
|
岗位方向 |
核心掌握模块 |
深入方向 |
|
后端开发 |
基础语法、事务、索引、JOIN 优化 |
执行计划、锁机制、分库分表 |
|
数据分析 |
聚合、窗口函数、CTE、业务建模 |
漏斗/留存分析、多维汇总、性能优化 |
|
数仓开发 |
数仓建模、ETL 逻辑、增量计算 |
分层设计、数据质量、性能治理 |
|
分析工程师 |
全栈 SQL + 工程化工具 |
dbt 项目、数据治理、CI/CD |
0.3 知识地图(完整版)
|
基础层: 关系思维 → 过滤排序 → 聚合分组 → 多表 JOIN → NULL 处理 → 集合运算 ↓ 中级层: 子查询 → CTE 分层 → 条件聚合 → 窗口函数 → 视图/事务/索引直觉 ↓ 高级层: 执行计划与优化 → 数据建模(OLTP/OLAP) → 半结构化/JSON → 方言适配 → 高级特性 ↓ 工程层: 数据质量测试 → 增量与幂等设计 → dbt 工具链 → 成本治理 → 安全与合规 |
第 01 章 · 心智模型
1.1 SQL 的声明式本质
SQL 是声明式语言:你描述「想要什么结果集合」,数据库优化器决定「怎么算、用什么路径算」。
- 命令式思维(程序循环):先取用户列表 → 循环每个用户查订单 → 累加金额 → 过滤结果
- SQL 思维:声明「按用户维度聚合订单金额,保留金额大于 1000 的用户」
新手常犯错误:用循环逻辑翻译 SQL,导致写出大量相关子查询、游标,性能极差且易出错。
1.2 结果集思维三问(写 SQL 前必过)
写任何查询前,先问自己三个问题,能避免 80% 的逻辑错误:
1.3 逻辑执行顺序(必记)
书写顺序
|
SELECT → DISTINCT → FROM → JOIN → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT/OFFSET |
引擎逻辑执行顺序(从先到后)
经典问题解答
- 为什么 WHERE 不能用 SELECT 别名?因为 WHERE 执行在 SELECT 之前。
- 为什么聚合过滤要用 HAVING?因为 WHERE 执行在 GROUP BY 之前,还没有聚合结果。
- 为什么窗口函数不能写在 WHERE 里?因为窗口函数在 SELECT 阶段计算,WHERE 执行时还未计算。
1.4 OLTP vs OLAP vs HTAP
|
维度 |
OLTP(业务库) |
OLAP(分析/数仓) |
HTAP(混合负载) |
|
典型操作 |
点查、写入、事务 |
全表扫描、聚合、宽表关联 |
同时支持业务写入与实时分析 |
|
模型倾向 |
规范化(三范式),减少冗余 |
星型/雪花模型、宽表、反范式 |
兼顾规范化与分析性能 |
|
性能关注点 |
索引命中率、锁、隔离级别 |
扫描数据量、分区、并行度 |
读写隔离、资源隔离、列式存储 |
|
数据规模 |
行级,GB-TB 级 |
全量历史,TB-PB 级 |
可横向扩展,PB 级 |
|
代表引擎 |
PostgreSQL、MySQL |
DuckDB、Snowflake、ClickHouse |
TiDB、OceanBase、PolarDB |
第 02 章 · 关系模型与查询骨架(基础)
2.1 关系模型核心概念
- 表(关系):二维结构,由行和列组成
- 行(元组):一条完整的实体记录
- 列(属性):实体的一个特征,有严格的数据类型
- 主键(PK):唯一标识一行,非空且唯一
- 外键(FK):引用另一表的主键,表达实体间关联关系
- 约束:NOT NULL、UNIQUE、CHECK、FOREIGN KEY、DEFAULT
常见关系与实现:
- 一对一:共享主键,或一方持有唯一外键(UNIQUE + FK)
- 一对多:多方持有外键(最常见,如用户-订单)
- 多对多:通过中间表拆解为两个一对多(如学生-课程-选课表)
2.2 规范化与反范式
三范式核心目标:减少数据冗余,避免更新异常、插入异常、删除异常。
- 1NF:列不可再分
- 2NF:消除部分依赖,非主键列完全依赖主键
- 3NF:消除传递依赖,非主键列不依赖其他非主键列
分析场景的反范式设计: 数仓与宽表场景常故意冗余维度信息(如把用户城市、等级冗余到订单表),用空间换查询效率,减少 JOIN 次数。
2.3 查询基础骨架
SELECT
column1,
column2 * 0.8 AS discount_price, — 表达式与别名
CONCAT(first_name, ' ', last_name) AS full_name
FROM schema.table_name
WHERE status = 'active'
AND created_at >= '2026-01-01'
ORDER BY created_at DESC
LIMIT 100;
要点补充:
- 生产环境禁止 SELECT *:拉取无关列增加 IO,大表宽表会严重拖慢性能,且表结构变更时易出问题。
- DISTINCT 成本很高:它需要排序或哈希去重,大表上慎用;先想清楚重复行的来源,优先通过过滤或 JOIN 优化消除重复。
- 别名可读性优先:业务含义明确,避免无意义的 a、b、c 别名。
2.4 过滤条件(WHERE)
常用谓词
- 比较:=、<>/!=、<、>、<=、>=
- 逻辑:AND、OR、NOT(注意优先级:NOT > AND > OR,复杂条件务必加括号)
- 集合:IN (…)、BETWEEN … AND …(闭区间)
- 模糊匹配:LIKE(%匹配任意字符,_匹配单个字符);PostgreSQL 支持 ILIKE 忽略大小写
- 空值判断:只能用 IS NULL / IS NOT NULL,= NULL 永远不成立
日期过滤黄金原则:左闭右开
尽量使用 >= 起始 和 < 结束+1 的写法,避免 DATE() 函数包裹索引列导致索引失效。
|
— 推荐:左闭右开,可命中 created_at 索引 WHERE created_at >= '2026-01-01' — 不推荐:函数包裹列,索引失效 WHERE DATE(created_at) BETWEEN '2026-01-01' AND '2026-01-31' |
2.5 NULL 与三值逻辑(基础第一大坑)
SQL 条件结果有三种:TRUE / FALSE / UNKNOWN,WHERE 只保留 TRUE 的行。
核心规则
2.6 排序与截取
ORDER BY revenue DESC NULLS LAST, user_id ASC
LIMIT 10 OFFSET 20; — 跳过前20条,取10条
- 无稳定 ORDER BY 时,LIMIT 结果不确定,分页可能出现重复或漏数据。
- 大偏移量 OFFSET 性能差:OFFSET 100000 需要先扫描 100000 行再丢弃,深分页推荐用「游标分页」(基于上一页最后一个 ID 过滤)。
2.7 常用数据类型与转换
- 字符串:拼接、大小写转换、截取、替换、正则匹配
- 数值:四则运算、取整(ROUND/FLOOR/CEIL)、绝对值、百分比计算
- 日期时间:加减间隔、截断(DATE_TRUNC)、时区转换(注意 TIMESTAMP WITH TIME ZONE)
- 类型转换:标准写法 CAST(x AS type),PostgreSQL 支持简写 x::type
隐式转换是性能与正确性的隐形杀手:字符串与数字比较、不同时间类型比较,都可能导致索引失效或结果错误。
第 03 章 · 过滤聚合与 JOIN(基础)
3.1 聚合函数
工作中最常用的五大聚合:COUNT / SUM / AVG / MIN / MAX
SELECT
user_id,
COUNT(*) AS order_cnt,
SUM(amount) AS total_revenue,
AVG(amount) AS avg_order_amount,
MIN(amount) AS min_amount,
MAX(amount) AS max_amount
FROM orders
WHERE status = 'paid'
GROUP BY user_id;
规则
- SELECT 中的非聚合列,必须出现在 GROUP BY 中(严格 SQL 模式下)。
- GROUP BY 的列决定了「输出一行代表什么」,即最终结果的粒度。
- 可按多列分组:GROUP BY user_id, date 代表「每个用户每天一行」。
3.2 HAVING:分组后过滤
SELECT user_id, SUM(amount) AS revenue
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 1000;
|
子句 |
过滤时机 |
可引用的内容 |
|
WHERE |
分组前,行级 |
原始列,不能用聚合函数 |
|
HAVING |
分组后,组级 |
聚合函数、GROUP BY 列 |
优化建议:能用 WHERE 提前过滤的,不要放到 HAVING。先缩小数据范围再聚合,性能更好。
3.3 JOIN:基础分水岭
3.3.1 JOIN 类型与行数直觉
|
类型 |
含义 |
行数变化直觉 |
|
INNER JOIN |
只保留两边都匹配的行 |
≤ 两侧原始行数 |
|
LEFT JOIN |
保留左表全部,右表不匹配则补 NULL |
≥ 左表行数(一对多会膨胀) |
|
RIGHT JOIN |
保留右表全部,左表不匹配则补 NULL |
可用 LEFT JOIN 改写,少用 |
|
FULL OUTER JOIN |
两侧未匹配的都保留,对方补 NULL |
≥ 两侧中较大的行数 |
|
CROSS JOIN |
笛卡尔积,左表每一行匹配右表每一行 |
左表行数 × 右表行数,慎用 |
3.3.2 ON vs WHERE(OUTER JOIN 核心坑)
对 LEFT JOIN 而言:
- 条件写在 ON 里:先按条件匹配右表,不匹配的左表行仍保留,右表列补 NULL。
- 条件写在 WHERE 里:JOIN 完成后再过滤,会把右表为 NULL 的行过滤掉,效果接近 INNER JOIN。
|
— 结果:所有用户都出现,2026年无订单的用户订单列为NULL SELECT u.user_id, o.order_id — 结果:只有2026年有订单的用户才出现,等价于INNER JOIN SELECT u.user_id, o.order_id |
3.3.3 行数膨胀与粒度对齐
一对多 JOIN 会复制左表行(如一个用户 3 个订单,用户字段会重复 3 次)。
- 错误做法:JOIN 明细后直接 SUM 用户维度的金额,导致金额被放大 N 倍。
- 正确做法:先聚合到相同粒度,再 JOIN;或先去重再关联。
3.4 多表关联经典练习
基于用户表 users、订单表 orders、订单明细表 order_items:
第 04 章 · 集合运算与 DDL/DML(基础)
4.1 集合运算
把两个查询结果集当作集合操作,要求两侧列数一致、类型兼容。
|
— UNION:去重合并(性能差,需要排序去重) SELECT user_id FROM orders_2025 — UNION ALL:不去重合并(性能好,优先使用) SELECT user_id FROM orders_2025 — INTERSECT:交集 SELECT user_id FROM paid_users — EXCEPT:差集(A 有 B 没有) SELECT user_id FROM all_users |
要点:
- 优先用 UNION ALL,确定需要去重时才用 UNION。
- 列名跟随左侧查询,ORDER BY 只能写在最外层。
- 分析场景常用 UNION ALL 拼接分区表、不同月份的分表。
4.2 NULL 在集合中的坑
- NOT IN (子查询) 若子查询结果包含 NULL,整个条件结果为 UNKNOWN,查询返回空集,这是经典易错点。
- 反连接场景优先使用 NOT EXISTS 或 LEFT JOIN … IS NULL,性能与正确性都更优。
4.3 DDL:数据定义语言
建表示例(PostgreSQL 风格)
CREATE TABLE users (
user_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
email TEXT NOT NULL UNIQUE,
nickname TEXT,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'blocked', 'deleted')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
常见操作
|
— 加列 ALTER TABLE users ADD COLUMN last_login_at TIMESTAMPTZ; — 删表 DROP TABLE IF EXISTS tmp_import; — 建索引 CREATE INDEX idx_users_status ON users(status); |
生产环境注意:
- 大表 ALTER TABLE 可能锁表,导致业务不可用,使用在线 DDL 工具(如 PostgreSQL 的 pg_repack)。
- 临时表命名规范,用完及时清理,避免空间占用。
4.4 DML:数据操作语言
|
— 插入 INSERT INTO users (email, nickname) VALUES ('a@example.com', 'Alice'); — 批量插入 INSERT INTO users (email, nickname) — 更新 UPDATE users — 删除 DELETE FROM users WHERE status = 'deleted'; |
进阶写法(方言差异大)
- UPSERT:插入或更新(PostgreSQL: ON CONFLICT … DO UPDATE;MySQL: INSERT … ON DUPLICATE KEY UPDATE)
- 多表更新:UPDATE … FROM … 关联其他表更新
- MERGE:SQL 标准语法,同时支持插入/更新/删除,数仓场景常用
第 05 章 · 子查询与 CTE(中级)
5.1 子查询类型
标量子查询
返回单个值,可出现在 SELECT、WHERE 中:
SELECT *
FROM products
WHERE price > (SELECT AVG(price) FROM products);
表子查询
返回多行多列,作为临时表放在 FROM 后或 IN 中:
SELECT u.*
FROM users u
JOIN (
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
) o ON u.user_id = o.user_id
WHERE o.total > 1000;
相关子查询
内层引用外层的列,逐行执行,性能通常较差:
— 每个用户的最新订单金额(相关子查询写法,不推荐)
SELECT
u.user_id,
(SELECT amount FROM orders o WHERE o.user_id = u.user_id ORDER BY order_date DESC LIMIT 1)
FROM users u;
大多数相关子查询可改写为 JOIN 或窗口函数,可读性与性能更好。
EXISTS / NOT EXISTS(半连接)
判断子查询是否有匹配行,性能优异,是反连接的首选写法:
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
AND o.amount > 1000
);
5.2 CTE:公共表表达式(2026 中级标配)
用 WITH 给中间结果命名,把复杂查询拆成可读的步骤:
WITH orders_2026 AS (
SELECT * FROM orders
WHERE order_date >= '2026-01-01'
),
user_revenue AS (
SELECT user_id, SUM(amount) AS revenue
FROM orders_2026
GROUP BY user_id
)
SELECT * FROM user_revenue
WHERE revenue > 1000
ORDER BY revenue DESC;
CTE 的价值
优化器注意事项
- PostgreSQL 12 及以上版本:普通 CTE 可被优化器内联,和子查询性能一致。
- 旧版本 PostgreSQL:CTE 是「优化围墙」,优化器无法下推条件,大表场景可能性能下降。
- DuckDB、Snowflake 等分析引擎:CTE 优化成熟,放心使用。
5.3 递归 CTE
用于处理树状结构、层级关系、路径展开(如组织架构、分类树、BOM):
WITH RECURSIVE employee_tree AS (
— 锚点:根节点
SELECT employee_id, manager_id, name, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
— 递归部分:连接上一层结果
SELECT e.employee_id, e.manager_id, e.name, t.level + 1
FROM employees e
JOIN employee_tree t ON e.manager_id = t.employee_id
)
SELECT * FROM employee_tree;
- 必须包含锚点 + 递归部分,用 UNION ALL 连接。
- 注意防止死循环:部分引擎支持 CYCLE 子句检测环。
5.4 CASE 与条件聚合
标准 CASE 写法(全方言通用)
SELECT
user_id,
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_amount,
SUM(CASE WHEN status = 'refunded' THEN amount ELSE 0 END) AS refund_amount,
COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_order_cnt
FROM orders
GROUP BY user_id;
FILTER 子句(更简洁,PostgreSQL/DuckDB 支持)
SELECT
user_id,
SUM(amount) FILTER (WHERE status = 'paid') AS paid_amount,
COUNT(*) FILTER (WHERE status = 'refunded') AS refund_cnt
FROM orders
GROUP BY user_id;
第 06 章 · 窗口函数(中级核心)
窗口函数是数据分析岗位的核心分界线,它在不塌缩行数的前提下,实现分组内计算。
6.1 基本语法框架
函数名(参数) OVER (
PARTITION BY 分组列 — 窗口按什么分组,类似 GROUP BY 但不合并行
ORDER BY 排序列 — 窗口内的排序,排名、累计、环比依赖它
ROWS/RANGE 帧范围 — 窗口覆盖哪些行,累计与滑动平均关键
)
6.2 排名类窗口函数
|
函数 |
特点 |
并列名次处理 |
|
ROW_NUMBER() |
强制连续序号,并列也按顺序排 1,2,3 |
不重复 |
|
RANK() |
并列同名次,下一名跳号:1,1,3 |
跳号 |
|
DENSE_RANK() |
并列同名次,下一名不跳号:1,1,2 |
不跳号 |
|
NTILE(n) |
把数据分成 n 个桶,返回桶编号 |
均分分组 |
经典场景:取每个用户最新的一笔订单
WITH ranked_orders AS (
SELECT
user_id, order_id, amount, order_date,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY order_date DESC, order_id DESC
) AS rn
FROM orders
)
SELECT * FROM ranked_orders WHERE rn = 1;
6.3 取值类窗口函数
|
函数 |
作用 |
|
LAG(col, n) |
取当前行往前第 n 行的值(环比常用) |
|
LEAD(col, n) |
取当前行往后第 n 行的值 |
|
FIRST_VALUE(col) |
取窗口内第一行的值 |
|
LAST_VALUE(col) |
取窗口内最后一行的值(注意帧范围) |
|
NTH_VALUE(col, n) |
取窗口内第 n 行的值 |
经典场景:日销售额环比
SELECT
order_date,
revenue,
LAG(revenue) OVER (ORDER BY order_date) AS prev_day_revenue,
ROUND(
(revenue – LAG(revenue) OVER (ORDER BY order_date))
/ LAG(revenue) OVER (ORDER BY order_date) * 100,
2
) AS wow_rate
FROM daily_revenue;
6.4 累计与滑动窗口
帧子句三种类型
常用帧写法
— 累计求和:从窗口开头到当前行(默认帧,ORDER BY 后默认就是这个)
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
— 近 7 天滑动平均(物理行)
AVG(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS avg_7d
注意:只写 ORDER BY 不写帧子句时,默认帧是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,排序键有重复值时,ROWS 和 RANGE 结果不同,需特别注意。
6.5 聚合类窗口函数
SUM / AVG / COUNT / MIN / MAX 都可以作为窗口函数使用,实现「明细行 + 分组聚合值」同屏展示
SELECT
order_id, user_id, amount,
SUM(amount) OVER (PARTITION BY user_id) AS user_total_amount,
amount / SUM(amount) OVER (PARTITION BY user_id) AS user_amount_ratio
FROM orders;
6.6 窗口函数 vs GROUP BY
|
维度 |
GROUP BY |
窗口函数 |
|
行数变化 |
塌缩,每组一行 |
不变,保留原始明细行数 |
|
输出内容 |
只有分组键与聚合值 |
明细 + 窗口计算值 |
|
典型用途 |
汇总报表、指标统计 |
排名、环比、累计、打标过滤 |
|
组合使用 |
可先窗口打标,再聚合 |
可嵌套在 CTE 中多层计算 |
6.7 高频业务题型
第 07 章 · 视图、事务与索引直觉(中级)
7.1 视图(VIEW)
视图是保存起来的查询定义,使用起来像表一样:
CREATE VIEW v_user_monthly_revenue AS
SELECT
user_id,
DATE_TRUNC('month', order_date) AS order_month,
SUM(amount) AS revenue
FROM orders
GROUP BY user_id, DATE_TRUNC('month', order_date);
作用
- 逻辑复用:复杂查询封装,上层调用简单
- 权限隔离:只给用户开放部分列/部分行
- 简化上层查询:隐藏复杂的 JOIN 和聚合逻辑
注意事项
- 视图本身不存储数据(除物化视图),查询时执行底层 SQL,复杂视图可能性能很差。
- 不要嵌套过深视图,多层嵌套会导致优化器失效,性能指数级下降。
物化视图(Materialized View)
预计算结果并落盘,查询直接读结果,换查询速度。需要定时刷新,适用于变化不频繁的汇总指标。
- PostgreSQL 原生支持,可手动刷新
- 数仓引擎(Snowflake、ClickHouse)有更丰富的物化视图能力
7.2 事务与 ACID
事务是一组 SQL 操作的集合,要么全部成功,要么全部失败。
ACID 四大特性
- 原子性(Atomicity):事务是不可分割的最小单元,全成功或全回滚
- 一致性(Consistency):事务前后,数据始终保持业务规则一致
- 隔离性(Isolation):多个事务并发执行,互相不干扰
- 持久性(Durability):事务提交后,数据永久生效,宕机不丢失
事务语法
BEGIN; — 开启事务
UPDATE accounts SET balance = balance – 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; — 提交事务
— ROLLBACK; — 回滚事务
隔离级别与并发问题
从弱到强:读未提交 → 读已提交(RC,大多数数据库默认) → 可重复读(RR,MySQL InnoDB 默认) → 可串行化。
对应并发问题:
- 脏读:读到其他事务未提交的数据
- 不可重复读:同一事务内,两次读同一行结果不同
- 幻读:同一事务内,两次范围查询行数不同
OLTP 业务必须理解事务边界与隔离级别;纯分析批处理更关注作业幂等与分区覆盖,而非行级锁细节。
7.3 索引直觉
索引是「用空间换时间」的数据结构,帮助数据库快速定位行。
最常见类型与适用场景
|
索引类型 |
底层结构 |
适用场景 |
代表引擎 |
|
B-Tree 索引 |
B+树 |
等值查询、范围查询、排序、JOIN 键,最通用 |
所有关系型数据库 |
|
哈希索引 |
哈希表 |
仅等值查询,速度极快,不支持范围与排序 |
MySQL Memory、PostgreSQL |
|
GIN 索引 |
倒排索引 |
数组、JSONB、全文搜索,多值匹配 |
PostgreSQL |
|
GiST 索引 |
通用搜索树 |
地理空间、全文搜索、模糊查询 |
PostgreSQL |
|
BRIN 索引 |
块范围索引 |
时序数据、有序大表,体积极小 |
PostgreSQL |
|
位图索引 |
位图 |
低基数列(性别、状态),数据仓库 |
Oracle、Greenplum |
索引失效常见场景
复合索引与最左前缀原则
复合索引 (a, b, c):
- 支持查询:a、a+b、a+b+c
- 不支持:b、c、b+c
- 等值查询列放前面,范围查询列放后面
第 08 章 · 查询优化与数据建模(高级)
8.1 先正确,再快
优化的前提是逻辑正确:粒度准确、JOIN 不膨胀、NULL 处理符合业务、边界条件完整。 错误的快查询没有任何价值,甚至会带来业务损失。
8.2 执行计划解读
使用 EXPLAIN ANALYZE 查看实际执行路径与耗时(PostgreSQL 示例):
EXPLAIN ANALYZE
SELECT user_id, SUM(amount)
FROM orders
WHERE order_date >= '2026-01-01'
GROUP BY user_id;
关注核心点
- Nested Loop:小表驱动大表,有索引时快
- Hash Join:大表关联,无索引时常用,OLAP 主流
- Merge Join:两个有序数据集关联,排序后合并
8.3 常见病灶与优化手段
云数仓额外关注:扫描字节数、分区裁剪是否生效、聚类键是否合理,这些直接等于计算成本。
8.4 OLTP 建模原则
- 遵循三范式,减少冗余,避免更新异常
- 明确主键、外键与约束,保证数据一致性
- 围绕事务与点查设计,避免大表宽表
- 适当预留扩展字段,应对业务迭代
8.5 OLAP 数仓建模
星型模型
- 事实表:度量业务事件(订单、点击、支付),包含外键与数值度量,先定粒度
- 维度表:描述实体属性(用户、商品、时间、渠道),主键关联事实表
缓慢变化维(SCD)
维度属性发生变化时的处理方式:
- SCD Type 1:覆盖更新,不保留历史
- SCD Type 2:新增行,用生效时间/版本号区分历史(最常用)
- SCD Type 3:新增列保存上一个值
数仓分层思想
|
ODS 原始层 → DWD 明细层(清洗、脱敏、标准化) → DWS 汇总层(轻度聚合) → ADS 应用层(报表/指标) |
每一层只做一类事,SQL 逻辑清晰,血缘明确,便于维护与排查问题。
8.6 粒度(Grain)——高级逻辑杀手
错误示例:订单事实表粒度是「一笔订单一行」,JOIN 「订单优惠明细表」(一笔订单多个优惠)后,直接 SUM(order_amount),订单金额被放大了优惠条目数倍。
正确对齐步骤:
第 09 章 · 现代特性与方言版图(高级)
9.1 高级 SQL 语言特性
多维汇总 GROUPING SETS
一次查询生成多个维度的聚合结果,替代多次 UNION ALL:
SELECT region, category, SUM(amount) AS total
FROM sales
GROUP BY GROUPING SETS (
(region, category), — 区域+类目
(region), — 区域汇总
() — 全局总计
);
还有 ROLLUP(层级汇总)、CUBE(全维度组合)两种简化写法。
行模式匹配 MATCH_RECOGNIZE
SQL:2016 引入,在有序数据上识别行为模式(如连续登录、会话切割、用户路径),部分引擎支持。
过程化能力
存储过程、自定义函数(UDF)方言差异极大。原则:能用集合写法解决,就不要写逐行循环的存储过程,性能与可维护性都差。
9.2 JSON 与半结构化数据
SQL:2016 引入原生 JSON 支持,SQL:2023 进一步强化。现实中大量事件日志、接口数据都是 JSON 格式。
常见操作
- 存储:文本 / 原生 JSON / 二进制 JSON(PostgreSQL JSONB,推荐)
- 提取:按路径取值,jsonb_extract_path_text、->、->> 操作符
- 展开:数组转行,jsonb_array_elements、JSON_TABLE
- 构造:从行数据构造 JSON 对象
- 索引:GIN 索引加速 JSON 键值查询
9.3 复杂类型:数组 / Map / Struct
BigQuery、Spark SQL、DuckDB 等分析引擎广泛支持嵌套类型:
- ARRAY 数组:多值属性,如用户标签列表
- Map 键值对:动态属性
- Struct 结构体:复合属性,如用户地址包含省、市、区
分析场景常需要「爆炸展开」(UNNEST)后再关联聚合。
9.4 属性图查询 SQL/PGQ
SQL:2023 新增 Part 16,把表数据当作属性图,用 GRAPH_TABLE + MATCH 做路径模式查询,减少超深 JOIN 的表达负担。 2026 年现状:标准已定,主流引擎落地进度不一,属于前沿了解项,团队未采用时不必深挖。
9.5 主流引擎方言版图
|
场景 |
代表引擎 |
复习重点 |
|
通用 OLTP |
PostgreSQL |
JSONB、窗口函数、索引类型、事务 |
|
Web 生态 OLTP |
MySQL |
索引、InnoDB 特性、事务隔离 |
|
本地分析 |
DuckDB |
友好语法、列式执行、文件查询 |
|
云数仓 |
Snowflake / BigQuery |
分区、聚类、扫描成本、半结构化 |
|
实时分析 |
ClickHouse / StarRocks |
列式存储、物化视图、聚合优化 |
|
湖仓一体 |
Spark SQL / Databricks |
类型系统、ANSI 模式、UDF |
|
分布式 HTAP |
TiDB / OceanBase |
兼容 MySQL/Oracle、分布式事务 |
DuckDB 友好语法示例(了解)
- GROUP BY ALL / ORDER BY ALL:自动按 SELECT 中非聚合列分组/排序
- SELECT * EXCEPT (col1, col2):排除指定列
- FROM-first 写法:先写 FROM 再写 SELECT,编辑器补全更友好
- 跨引擎迁移时收敛到标准 SQL,避免方言依赖
9.6 跨方言迁移检查表
第 10 章 · 分析工程(高级延伸)
分析工程把「写 SQL」升级为「工程化交付数据产品」,是数据团队规模化的必经之路。
10.1 SQL 在数据管道中的位置
典型数据链路:
|
业务源系统 → 数据同步(CDC/ETL) → 数仓 ODS → DWD/DWS 转换(SQL/dbt) → ADS 指标层 → 报表/BI/应用 |
你写的 CTE 分层逻辑,落到工程里就是模型分层与依赖图。
10.2 数据质量:用 SQL 做断言
高质量数据是分析可信的基础,至少覆盖四类测试:
在 dbt 中体现为:unique、not_null、relationships、自定义 SQL 测试。
10.3 增量、幂等与迟到数据
|
概念 |
含义 |
|
增量 |
每次只处理新增/变更数据,而非全量重算,节省资源 |
|
幂等 |
同一批数据跑多次,结果完全一致,可重复执行 |
|
迟到数据 |
事件发生时间晚于数据到达时间,需要回补历史窗口 |
常见实现策略:
- 按日期分区覆盖:每次重跑当天/近 N 天分区
- Merge 按业务键更新:主键存在则更新,不存在则插入
- 水位线(Watermark):定义迟到阈值,超过阈值不再更新
10.4 dbt 在 2026 年的发展
dbt 是分析工程的事实标准工具,核心是「SQL 为中心 + 版本控制 + 测试 + 文档 + 血缘」。
2026 年核心演进:
10.5 可读性与协作规范
10.6 AI 时代的 SQL 能力
AI 可以快速生成初稿,但审计 SQL、验证结果、把控口径才是人的核心竞争力。 AI 生成 SQL 必查清单:
10.7 安全与成本意识
- 最小权限原则:账号只给必要的查询权限,禁止生产库高权限
- 防 SQL 注入:业务代码中禁止字符串拼接 SQL,使用参数化查询
- 数据脱敏:敏感字段(手机号、身份证、金额)分级脱敏
- 行级安全(RLS):不同用户只能看到自己权限范围内的数据
- 成本治理:云上数仓按扫描量计费,避免无意义的全表扫描,合理设置分区与聚类
附录 A:SQL 避坑速查表
网硕互联帮助中心




评论前必须登录!
注册