汇总 SQL 的 JOIN、聚合与分组、窗口函数、CTE、索引和事务用法,附可直接改写的语句模板,适合写报表查询与排查慢 SQL 时参考。
以下示例基于通用 SQL,MySQL 8 与 PostgreSQL 均可参考。
JOIN
内连接只返回两表都匹配的行,左连接保留左表全部行,右表没匹配上的列补空值;连接条件写 ON,结果过滤写 WHERE。把右表条件放进 WHERE 会让左连接退化成内连接,这是常见坑。
SELECT * FROM orders WHERE id = 1;
聚合与分组
聚合函数忽略空值,WHERE 在分组前过滤原始行,HAVING 在分组后过滤结果,排序可以直接使用聚合别名。统计行数用 COUNT(*),只数非空值用 COUNT 加列名,两者别混用。
SELECT COUNT(*) FROM orders;
窗口函数
OVER 子句不压缩行数,分区子句分组、排序子句定序,适合做组内排名、累计求和和同比计算。连续序号与并列排名遇到相同值会给出不同结果,取前三名要先想清楚用哪一个。
SELECT ROW_NUMBER() OVER () AS rn FROM t;
CTE
WITH 把子查询提成命名结果集,多段引用比嵌套子查询好读;递归 CTE 还能展开树形结构,比如组织架构。复杂报表可以串多个 CTE,每段只做一件事,排查时逐段单独执行即可。
WITH p AS (SELECT 1) SELECT * FROM p;
索引
联合索引遵循最左前缀原则,用 EXPLAIN 确认是否命中;在索引列上套函数会让索引失效,范围条件尽量往后面放。索引不是越多越好,写入变慢和占用空间都要算进成本里。
CREATE INDEX i ON orders (id);
事务
事务保证一组操作要么全成功要么全回滚,转账场景先用 FOR UPDATE 锁住要改的行,校验余额无误后再提交。锁的范围尽量小,长事务会拖住其它请求,出错时记得执行回滚释放。
START TRANSACTION;