一、先定几条硬规则
- 关键字大写,表名、字段名小写(或全小写加下划线),让关键字和数据一眼可分。
- 每个主要子句独占一行:
SELECT / FROM / WHERE / GROUP BY / HAVING / ORDER BY / LIMIT。 - 缩进 2 或 4 空格,同一层对齐,不用 Tab。
- 逗号放行尾,不要放行首(行首逗号改一行会影响上下两行)。
- JOIN 单独成行,
ON条件再缩进一层。
二、格式化前后对比
-- 格式化前
select a.id,a.name,count(b.id) cnt from users a left join orders b on a.id=b.uid where a.status=1 and b.created_at>'2026-01-01' group by a.id,a.name order by cnt desc limit 10;
-- 格式化后
SELECT
a.id,
a.name,
COUNT(b.id) AS cnt
FROM users AS a
LEFT JOIN orders AS b
ON a.id = b.uid
WHERE a.status = 1
AND b.created_at > '2026-01-01'
GROUP BY a.id, a.name
ORDER BY cnt DESC
LIMIT 10;
第二种写法里,LEFT JOIN ... ON 的配对关系一目了然;第一种要读到中间才知道 b 是怎么来的。
三、JOIN 与子查询的排版
多表关联时,按「主表 → 关联表」顺序排列,每个 ON 紧跟它的 JOIN:
FROM orders AS o
JOIN users AS u
ON u.id = o.user_id
JOIN order_items AS i
ON i.order_id = o.id
子查询要么整体缩进,要么提成 CTE。可读性上 CTE 更好,也便于单独调试:
WITH paid AS (
SELECT id, user_id FROM orders WHERE status = 'paid'
)
SELECT u.name, COUNT(*) AS cnt
FROM paid AS p
JOIN users AS u ON u.id = p.user_id
GROUP BY u.name;
四、几个高频坏味道
- **
SELECT ***:字段变了就悄悄出错,且无法走覆盖索引。 IN列表过长:几十个值堆一行,改成每行一个值并折行,或改成临时表/JOIN。- 隐式类型转换:
WHERE phone = 13800138000(字段是 varchar)会让索引失效,字符串必须加引号。 - 在索引列上用函数:
WHERE DATE(created_at) = '2026-11-03'走不了索引,应改写成范围查询。 WHERE里写1=1拼接:动态 SQL 常见,但格式化后要能看清真正的条件边界。
五、格式化不会改变语义,但有一个例外
字符串字面量内部的空格、换行不能动。WHERE name = 'a b' 里的两个空格是数据的一部分。好的格式化工具会保护引号内内容,手写替换时要注意。
另外注释风格也要统一:-- 单行注释放在语句上方,不要塞在行尾解释一个复杂表达式——行尾注释在格式化换行后容易错位。
六、为什么值得花这个时间
- 排查慢查询:
EXPLAIN的输出要和原 SQL 对照,结构乱的 SQL 根本对不上。 - Code Review:JOIN 少个条件、
WHERE放到GROUP BY后面,格式清晰时 reviewer 一眼能看见。 - 事故复盘:一条导致全表更新的
UPDATE,往往在格式化后才会发现WHERE那行被注释掉了。
日常清理长 SQL 用 SQL 格式化工具,粘贴即排版,本地处理不上传;格式化只改排版,不会执行任何语句。
小结
关键字大写、子句换行、JOIN 与 ON 成对缩进、逗号放行尾。格式化的价值不在美观,而在于让「漏条件」「错位置」这类错误无处藏身。