跳到主要內容

← 返回部落格

為什麼要格式化 SQL(以及格式化工具到底做了什麼)

ORM 印出一段查詢,它以一行 300 字元的姿態落在你的 console 裡。格式化工具把那面文字牆變成有縮排、掃一眼就懂的查詢,每個子句各就各位。是同一段查詢——只是變得看得懂了。以下說明它實際在做什麼。

一長行進去,看得懂的查詢出來。 我們給SQL 格式化工具這一整行:select u.id,u.name,count(o.id) as orders from users u left join orders o on o.user_id=u.id where u.active=1 group by u.id,u.name having count(o.id)>2 order by orders desc。回來的結果每個子句各佔一行、被選的欄位縮排在 select 底下,而最關鍵的是:它在 = 與 > 兩側補上了空白,所以 o.user_id=u.id 變成 o.user_id = u.id。注意它沒有把關鍵字改成大寫:那是另一個獨立選項,因為大小寫是風格選擇,而縮排不是。

格式化改了什麼(又沒改什麼)

格式化只動空白、換行與縮排——以及選擇性的關鍵字大小寫。它把 SELECT、FROM、WHERE、JOIN、GROUP BY 各自放到獨立的行,把欄位和條件縮排,讓巢狀結構一目了然。

它從不更動的是:你的資料表、欄位、值與邏輯。格式化後的查詢和擠成一團時回傳完全相同的結果。這也代表格式化不是驗證——它不會告訴你某個欄位拼錯了,或這段查詢實際上跑不跑得動。

方言:工具為什麼要問你用哪個資料庫

SQL 有標準,但每家資料庫都加了自己的味道,格式化工具需要知道是哪一種才能正確解析:

  • 識別字引號——MySQL 用反引號(`col`);PostgreSQL 與標準 SQL 用雙引號("col")。
  • 限制筆數——MySQL/SQLite/PostgreSQL 用 LIMIT,SQL Server 用 TOP。
  • 型別轉換——PostgreSQL 的 value::int 在別處沒有對應寫法。

選錯方言,這些就會被誤判,縮排也會跑掉。貼進來前,先在 SQL 格式化選你實際用的資料庫。

關鍵字大寫:是風格,不是規則

很多團隊把關鍵字寫成大寫(SELECT、FROM),讓它們和資料表、欄位名區隔開。SQL 本身對關鍵字不分大小寫,所以這純粹是可讀性慣例——格式化工具可以一鍵把整段查詢統一過來。

大小寫是我們的 SQL 格式化工具刻意不動的東西。一句小寫的查詢,格式化後:

select
  *
from
  users
where
  id = 1

同樣的形狀,用大寫打:

SELECT
  id,
  name
FROM
  t
WHERE
  a = 1
  AND b = 2

小寫的 select 保持小寫;大寫的 SELECT 保持大寫。預設是 preserve,因為改關鍵字大小寫是團隊風格,不是修正錯誤 —— 而且悄悄改掉它會讓「純格式化」那個 commit 的 diff 變成沒辦法審,而那是格式化工具最不該做的一件事。

為什麼在本機做?

前端格式化工具在你的瀏覽器裡解析並重新輸出 SQL。你的查詢——往往透露了資料表結構與商業邏輯——永遠不會離開你的電腦。

相關工具

格式化前後對照

同一段查詢,被 ORM 壓成一行後、再格式化的樣子:

select u.id,u.name,count(o.id) as orders from users u left join orders o on o.user_id=u.id where u.active=1 group by u.id,u.name having count(o.id)>0 order by orders desc;

格式化後,結構由上而下一目了然:

SELECT
    u.id,
    u.name,
    COUNT(o.id) AS orders
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.active = 1
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 0
ORDER BY orders DESC;

結果完全相同,但 JOIN、篩選、分組現在一眼就看得出來。

格式化真正會改的是換行落在哪裡。一個被壓成一行的 INSERT 回來是:

insert into
  logs (a, b)
values
  (1, 2)

子句關鍵字自己一行,它的運算元縮排在下面,而欄位清單的逗號留在行內。整個演算法就是這樣 —— 一行一個子句、運算元縮排 —— 一旦你能預測它,格式化過的檔案就能只靠形狀來審:一個沒有貼在最左邊的 WHERE,就是子查詢裡的 WHERE。

逗號放行尾還是行首

欄位常見兩種擺法:行尾逗號(每行結尾的 u.id,)或行首逗號(每行開頭的 , u.name)。行首逗號會讓版本控制的 diff 更乾淨——新增一個欄位只動一行,而不是兩行——但兩種都合法,多數格式化工具都可以選。同一個專案裡保持一致,比選哪一種更重要。

常見問題

  • 格式化會影響效能嗎? 不會。解析器會忽略無意義的空白與關鍵字大小寫,所以格式化後的查詢對引擎而言完全等價。唯一對空白敏感的,是字串常值的內容。
  • 格式化後的 SQL 該進版控嗎? 檢視表、遷移腳本、預存程序都建議這麼做——一致的風格能讓 diff 變小、審查更快。
  • 但格式化不會修好壞掉的查詢。 它只是重排文字;要抓錯,還是得靠真正的資料庫或 linter。之後要把結果匯出?看如何把 JSON 轉成 CSV 與 Excel。