写在前面
干后端开发的,谁没被几段「祖传SQL」折磨过?打开某个遗留系统的存储过程,一段两千行的 SQL 挤在一行里,没有缩进、关键字五颜六色地混在一起——你只能眯着眼睛艰难地辨认哪段是 SELECT、哪段是 WHERE。更可怕的是写这段代码的同事已经离职了,注释一个字没有。
SQL 格式化的意义不在于让代码「好看」,而在于让你一眼看清逻辑结构。看清结构了,才能改对代码、找到性能瓶颈、做代码 Review 的时候不放过每一个问题。
这篇文章不讲大道理,直接从实际场景出发,结合我站提供的 SQL 格式化工具,帮你建立一套 SQL 排版的工作流。
一、SQL 格式化到底在格式化什么?
先说清楚,格式化不是「变魔术」——它只改变排版,不改任何逻辑。格式化前后的 SQL 执行结果完全一样。
那么格式化工具到底做了什么?
| 处理项 | 说明 | 示例 |
|---|---|---|
| 缩进对齐 | 子句按层级缩进,FROM 对齐 SELECT,WHERE 对齐 FROM | 一眼看出查询层次 |
| 关键字大写 | SELECT/WHERE/FROM/JOIN 等关键字统一大写 | 关键字和字段名一目了然 |
| 换行分段 | 每个子句单独一行,CASE WHEN 分支换行 | 长 SQL 不再堆在一行 |
| 逗号前置/后置 | 字段列表的逗号放在行首还是行尾 | 按团队规范来 |
| 括号匹配 | 嵌套子查询的括号层级清晰对齐 | 不再数括号个数 |
随便说一个真实案例。我接手过一个电商系统的订单查询,原始 SQL 长这样:
SELECT o.id,o.order_no,u.name AS user_name,o.total_amount,o.status,o.created_at,
(select count(*) from order_items where order_id=o.id) as item_count,
(select sum(amount) from order_payments where order_id=o.id) as paid_amount
FROM orders o LEFT JOIN users u ON o.user_id=u.id WHERE o.created_at>'2025-01-01'
AND o.status IN ('paid','shipped') ORDER BY o.created_at DESC LIMIT 50;
一行写完,包含了子查询、LEFT JOIN、多条件 WHERE、ORDER BY。光看这一行,你很难判断 ON 条件写对了没,子查询的关联字段对不对。
用 SQL 格式化工具 格式化之后,秒变这样:
SELECT
o.id,
o.order_no,
u.name AS user_name,
o.total_amount,
o.status,
o.created_at,
(
SELECT
COUNT(*)
FROM
order_items
WHERE
order_id = o.id
) AS item_count,
(
SELECT
SUM(amount)
FROM
order_payments
WHERE
order_id = o.id
) AS paid_amount
FROM
orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE
o.created_at > '2025-01-01'
AND o.status IN ('paid', 'shipped')
ORDER BY
o.created_at DESC
LIMIT 50;
同样的 SQL,格式化之后一眼就能看清:两个子查询分别从哪张表取什么数据、LEFT JOIN 关联条件是什么、WHERE 筛选了哪些字段。改需求的时候,要在子查询里加字段,一秒定位。
二、SQL 格式化的核心规范
2.1 关键字大写,字段小写
这是最通用的规范,没有例外:
-- ✅ 推荐写法
SELECT name, email, created_at
FROM users
WHERE status = 'active'
AND deleted_at IS NULL;
-- ❌ 不推荐:全小写难读
select name, email, created_at
from users
where status = 'active'
and deleted_at is null;
-- ❌ 也不推荐:全大写像在吼
SELECT NAME, EMAIL, CREATED_AT
FROM USERS
WHERE STATUS = 'ACTIVE'
AND DELETED_AT IS NULL;
2.2 每个子句独立一行
这条规则在简单 SQL 里看起来有点「浪费行数」,但在复杂查询里,它是救命稻草:
-- ✅ 复杂查询分行排版
SELECT
category,
COUNT(*) AS product_count,
AVG(price) AS avg_price
FROM
products
WHERE
status = 'published'
GROUP BY
category
HAVING
COUNT(*) > 10
ORDER BY
product_count DESC;
2.3 JOIN 链要对齐
多表 JOIN 的时候,每张表单独一行,ON 条件缩进在 JOIN 下面:
SELECT
o.id,
o.order_no,
u.name,
p.amount AS payment_amount
FROM
orders o
INNER JOIN users u ON o.user_id = u.id
LEFT JOIN payments p ON o.id = p.order_id
AND p.status = 'success'
LEFT JOIN (
SELECT
order_id,
SUM(quantity) AS total_qty
FROM
order_items
GROUP BY
order_id
) oi ON o.id = oi.order_id
WHERE
o.created_at >= '2026-01-01';
注意看,AND p.status = 'success' 缩进在 LEFT JOIN 下面,表示它是 JOIN 条件的一部分,不是 WHERE 条件。这是很多人容易搞混的地方。
2.4 CASE WHEN 分支对齐
CASE WHEN 如果不排版,可读性极差:
-- ✅ 正确排版
SELECT
id,
name,
CASE
WHEN score >= 90 THEN '优秀'
WHEN score >= 80 THEN '良好'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END AS grade_level
FROM
exam_results;
-- ❌ 挤在一起很难读
SELECT id, name, CASE WHEN score>=90 THEN '优秀' WHEN score>=80 THEN '良好' ELSE '不及格' END
FROM exam_results;
三、不同数据库方言的格式化注意点
SQL 格式化工具虽然支持多种方言,但不同数据库有一些自己的语法特性,需要留心。
MySQL
MySQL 的 BACKTICK 反引号、GROUP_CONCAT 函数、LIMIT 子句位置都跟标准 SQL 不同:
-- MySQL 风格
SELECT
`users`.`id`,
`users`.`name`,
GROUP_CONCAT(`orders`.`order_no` SEPARATOR ', ') AS order_list
FROM
`users`
LEFT JOIN `orders` ON `users`.`id` = `orders`.`user_id`
GROUP BY
`users`.`id`
HAVING
COUNT(`orders`.`id`) > 0
ORDER BY
`users`.`created_at` DESC
LIMIT 20;
PostgreSQL
PostgreSQL 的 :: 类型转换、ILIKE 模糊匹配、DISTINCT ON 是特色:
-- PostgreSQL 风格
SELECT
DISTINCT ON (u.id)
u.id,
u.name,
u.created_at::date AS created_date,
array_agg(DISTINCT o.status) AS order_statuses
FROM
users u
JOIN orders o ON u.id = o.user_id
WHERE
u.email ILIKE '%@example.com'
ORDER BY
u.id,
o.created_at DESC;
SQL Server
SQL Server 的 TOP、WITH (NOLOCK)、方括号标识符要注意:
-- SQL Server 风格
SELECT TOP 50
[u].[id],
[u].[name],
[o].[total_amount]
FROM
[dbo].[users] [u] WITH (NOLOCK)
INNER JOIN [dbo].[orders] [o] WITH (NOLOCK)
ON [u].[id] = [o].[user_id]
WHERE
[u].[status] = 'active'
ORDER BY
[o].[created_at] DESC;
四、我的 SQL 格式化工作流
每天跟数据库打交道的我,已经形成了一套固定的 SQL 处理流程,分享给你参考:
步骤 1:粘贴 → 格式化
不管是自己写的还是从同事那接手的 SQL,第一件事就是粘贴到 SQL 格式化工具 点一下格式化。设置我推荐:
- 缩进:4 个空格(Tab 在不同编辑器里显示不一样)
- 关键字:大写(视觉区分度最高)
- 逗号:行首(方便增删字段)
步骤 2:审查逻辑
格式化之后,按顺序读一遍各个子句:
SELECT里的字段是不是都是必需的?(不要 SELECT *)FROM/JOIN的表关系对不对?(LEFT JOIN 还是 INNER JOIN)WHERE条件有没有遗漏索引?(函数包裹字段会导致索引失效)ORDER BY/LIMIT是否合理?
步骤 3:加注释
格式化后的 SQL 结构清晰,这时候加注释事半功倍:
-- 查询:月度销售报表(后台管理用)
-- 作者:运维团队
-- 修改:2026-05-27 增加退款排除逻辑
SELECT
DATE_TRUNC('month', o.created_at) AS month, -- 月份
COUNT(DISTINCT o.id) AS order_count, -- 订单数
SUM(o.total_amount) AS revenue, -- 总收入
SUM(o.discount_amount) AS total_discount, -- 总优惠
AVG(o.total_amount) AS avg_order_amount -- 客单价
FROM
orders o
INNER JOIN stores s ON o.store_id = s.id -- 关联门店
WHERE
o.status NOT IN ('cancelled', 'refunded') -- 已取消和已退款的排除
AND o.created_at BETWEEN '2026-01-01' AND '2026-01-31'
GROUP BY
month
ORDER BY
month;
步骤 4:性能检查
格式化之后把 SQL 扔进数据库跑 EXPLAIN,看看执行计划。格式清晰的 SQL,EXPLAIN 结果也容易对应到具体子句——哪一步走全表扫描、哪一步用了索引,一眼就能定位。
五、常见误区与注意事项
格式化不是性能优化工具。SQL 格式化只改变可读性,不会改变执行计划。别指望格式化之后查询变快了——那是索引和查询优化的事。
注意超大 SQL。如果一段 SQL 超过几百行,格式化后的内容可能很长。建议分段处理:先把外层查询格式化,确定结构无误后再处理嵌套的子查询。
存储过程和函数内部的 SQL 建议单独格式化。从存储过程里把核心 SQL 摘出来,格式化完再放回去,比整体格式化更容易控制。
团队统一规范比工具更重要。格式化工具的设置可以导出分享,建议团队用同一套配置,这样所有人的 SQL 看起来风格一致,代码 Review 也省心。
格式化后记得跑一遍测试。虽然理论上格式化不改逻辑,但如果原始 SQL 有语法问题(比如括号不匹配),格式化后可能暴露出来。把格式化后的 SQL 先在测试环境跑一遍,确保结果一致。
六、总结
SQL 格式化是一件「做了不觉得多厉害,不做天天被折磨」的事。它花不了你多少时间——粘贴一下、点一下按钮,三秒钟的事——但省下来的阅读和排错时间,是十倍百倍。
不管你用 SQL 格式化工具 还是其他工具,关键是要养成习惯:写 SQL 就格式化,接手 SQL 先格式化。把它当成你日常工作流的一部分,就像写代码要保存一样自然。
最后送你一句我贴在公司工位上的话:「你写的 SQL 不仅是给数据库执行的,更是给明天的自己看的。」