🏠 首页 攻略 SQL格式化实战指南:手把手教你写出整洁的查询语句

SQL格式化实战指南:手把手教你写出整洁的查询语句

还在忍受一坨乱麻般的SQL语句?本文从缩进规范、关键字大小写、复杂查询排版到在线SQL格式化工具的使用技巧,带你彻底告别SQL阅读噩梦。

写在前面

干后端开发的,谁没被几段「祖传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 的 TOPWITH (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:审查逻辑

格式化之后,按顺序读一遍各个子句:

  1. SELECT 里的字段是不是都是必需的?(不要 SELECT *)
  2. FROM/JOIN 的表关系对不对?(LEFT JOIN 还是 INNER JOIN)
  3. WHERE 条件有没有遗漏索引?(函数包裹字段会导致索引失效)
  4. 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 结果也容易对应到具体子句——哪一步走全表扫描、哪一步用了索引,一眼就能定位。


五、常见误区与注意事项

  1. 格式化不是性能优化工具。SQL 格式化只改变可读性,不会改变执行计划。别指望格式化之后查询变快了——那是索引和查询优化的事。

  2. 注意超大 SQL。如果一段 SQL 超过几百行,格式化后的内容可能很长。建议分段处理:先把外层查询格式化,确定结构无误后再处理嵌套的子查询。

  3. 存储过程和函数内部的 SQL 建议单独格式化。从存储过程里把核心 SQL 摘出来,格式化完再放回去,比整体格式化更容易控制。

  4. 团队统一规范比工具更重要。格式化工具的设置可以导出分享,建议团队用同一套配置,这样所有人的 SQL 看起来风格一致,代码 Review 也省心。

  5. 格式化后记得跑一遍测试。虽然理论上格式化不改逻辑,但如果原始 SQL 有语法问题(比如括号不匹配),格式化后可能暴露出来。把格式化后的 SQL 先在测试环境跑一遍,确保结果一致。


六、总结

SQL 格式化是一件「做了不觉得多厉害,不做天天被折磨」的事。它花不了你多少时间——粘贴一下、点一下按钮,三秒钟的事——但省下来的阅读和排错时间,是十倍百倍。

不管你用 SQL 格式化工具 还是其他工具,关键是要养成习惯:写 SQL 就格式化,接手 SQL 先格式化。把它当成你日常工作流的一部分,就像写代码要保存一样自然。

最后送你一句我贴在公司工位上的话:「你写的 SQL 不仅是给数据库执行的,更是给明天的自己看的。」