雜記 › 技術筆記 › 6 個元素不折、7 個才折:SQL 美化與壓縮對註解、關鍵字、IN 長清單和巢套子查詢各做了什麼

6 個元素不折、7 個才折:SQL 美化與壓縮對註解、關鍵字、IN 長清單和巢套子查詢各做了什麼

一、壓縮跑完,註解整段消失

一份跑了幾個月的報表 SQL,輸入 967 字元、14 行、3 條語句。按「美化」是 1376 字元、72 行,按「壓縮」是 914 字元、3 行。體積確實下去了,貼在語句頭部那兩行說明欄位口徑的註解卻一個字都不剩。

第二遍我先把「壓縮時刪除註解」的勾去掉再壓。同一條 SELECT a -- 行註解 換行 FROM t,勾著時輸出 15 字元 1 行,取消勾選才得到 24 字元的 SELECT a /* 行註解*/ FROM t。差的就是那個核取方塊的預設狀態——它預設勾著。

二、註解的落點有三種

站在語句外面的行註解留在原處:一段帶頭部說明的輸入是 23 字元,美化後仍是 5 行 31 字元。站在括號裡或 CASE 裡的會被就地改寫成區塊註解——輸入 SELECT f(a -- 注 是 29 字元,輸出 f(a /* 注*/) 的 38 字元,因為 -- 換成 /* */ 之後註解不再吞掉行尾;# 起頭走同一條分支。

第三種才是壓縮檔:勾著時行註解和區塊註解一起被跳過,取消勾選才拿到上面那句 24 字元。清單頁那句「壓縮時把行註解改寫成區塊註解」描述的是取消勾選之後的分支,不是預設分支。

三、IN 折行的判據是「嚴格大於」

閾值欄預設 6,判據是「逗號數加 1 大於閾值」,等於不折:預設閾值下 6 個元素待在一行,第 7 個才炸開。輸入框宣告 1 到 99,程式碼只兜下界、不兜上界——同一條 20 個元素的 IN,閾值填 99、100、999,輸出完全一樣,都是 6 行 111 字元。

100 個元素的清單,折與不折差得更遠:

  • 折行:107 行、1238 字元,每個元素獨佔一行,行首是 8 個字元的前綴。
  • 把閾值改成 100 讓它不折:6 行、432 字元。行數差 101,字元數差 806。
  • 閾值填 1 不等於永遠折行:2 個元素折成 9 行 68 字元,1 個元素的 IN 仍是 6 行 43 字元。
  • 帶子查詢的 IN 不看閾值:IN (SELECT b FROM u) 填 999 也展開成 11 行。
行數 107 行 6 行 字元數 1238 字元 432 字元 圖 1:100 個元素折與不折

四、「只規範空白」這一檔,6 個選項全不生效

它的實作直接拿 token 原文去拼,不讀選項物件,而且只有壓縮檔會把選項列置灰。於是縮排、大小寫、折行閾值、運算子空格、逗號前置、刪除註解這 6 項在這裡全是裝飾:實測把「運算子兩側空格」關掉,輸出仍是同一句 24 字元。

它也不補齊空格。x=1, y = 2, z= 3, w =4 進出都是 36 字元,只有多餘空白被歸一(26 字元縮成 18 字元),同一句換成「美化」是 7 行 60 字元。原本的行結構也保不住:行首縮排被抹光(43 字元那段出來 31 字元),空行被刪(4 行 23 字元變 2 行 15 字元)。這一檔只做了兩件事——歸一多餘空白、清掉行首與空行。

五、改不改大小寫,只看那兩張詞表

大小寫只動 SQ_KW 的 174 個詞和 SQ_FN 的 75 個函式名:myFun(1)、tab(a, b)、表名 TBL 原樣不動,而 a::text 會被抬成 a::TEXT。決定換行的是 SQ_OWN 的 17 項、SQ_TAIL 的 16 項加上 SQ_JOIN 的 11 項,合成 44 個片語。

名單咬人兩處。from 在關鍵字表裡而不在函式表裡,實測 SELECT from(1) 被當成子句換成 3 行、把 (1) 掛在 FROM 下面,「緊貼左括號就按函式呼叫處理」那句 FAQ 在這裡不成立。壓縮檔也不讀縮排、閾值、逗號前置與運算子空格,三種設定出來是同一行,大小寫卻照改,「壓縮只動空白」同樣不成立。

六、JOIN 的三詞缺口,與乘算的縮排

SQ_JOIN 的 11 項裡沒有三詞組合,NATURAL LEFT JOIN 落不進名單,被拆成 FROM、t1 NATURAL、LEFT JOIN t2 三段,NATURAL 留在 FROM 的內容行上,單寫的 OUTER JOIN 同樣認不出;LEFT OUTER JOIN(6 行、lvl=2、ON 再深一層)與 CROSS JOIN(5 行)都正常。

巢套不是加一層而是乘一層:3 層衍生表最深 lvl=5,縮排選 2、4、8 個空格與 Tab 時,最深行前綴分別是 10、20、40、5 個字元,整段字元數 123、191、327、89,行數一直是 14。官方示例的最大縮排層級只有 3,套到第三層再選 8 個空格,整段體積就翻倍以上。

2 空格 123 字元 前綴 10 4 空格 191 字元 前綴 20 8 空格 327 字元 前綴 40 Tab 89 字元 前綴 5 圖 2:縮排乘算的體積代價

七、建表語句不排版,佔位符緊貼等號會被切開

44 個片語裡沒有 CREATE TABLE、ALTER TABLE、CREATE INDEX、DROP TABLE:一份 6 行 4 欄、86 字元的建表語句,美化後塌成 1 行 74 字元,不報錯也不提示。PostgreSQL 的 $$ 引用更直接,SELECT $$ it's ok $$ 拋「' 引號沒有閉合」。

更隱蔽的是佔位符。WHERE b=? AND c=:uid AND d=@@global.x 裡 @ 和 : 都算運算子字元,緊貼等號時被併成一個 token,空格再插回 token 內部,輸出成 d =@@ global.x、c =: uid,變數名被劈成兩段;等號前留一個空格就正常,59 字元那句出來是 8 行 79 字元。

八、同一份示例的四檔成績

檔位結果字元數行數UTF-8 位元組
輸入原文967141101
美化1376721510
壓縮91431048
只規範空白956141090

統計列是 5 項、含「結果字元數」,示例這一行實測是 3 / 72 / 1376 / 3 / 967。美化再美化逐字相同(1376 變 1376),壓縮再壓縮也相同,但壓縮結果拿去美化會得 1378 字元、與直接美化差 2 字元——先壓再美化不是原路返回。

量級不用擔心:300135 字元、1819 條語句,美化用 164 毫秒得 49112 行、542060 字元,壓縮用 82 毫秒得 292858 字元。全程不發請求、不寫本機儲存,因此沒有歷史紀錄、沒有批次入口,只有 1 個輸入框;下載在內容前加 U+FEFF,落盤檔案比頁面字元數多 3 位元組。在 SQL 美化與壓縮 的三檔之間切換,要盯的是核取方塊和閾值。

Q:壓縮刪掉的註解還能找回來嗎?

只能取消「壓縮時刪除註解」的勾再壓一次,拿到 /* */ 那種 24 字元寫法;沒有本機儲存也沒有歷史,壓掉的原文不在你手上就找不回來。美化檔不吃註解,但括號與 CASE 內的行註解會被改成區塊註解,diff 會變大。

Q:閾值是不是越小越折、越大越不折?

方向對,邊界不對:判據是嚴格大於,1 個元素在閾值 1 下仍不折(6 行 43 字元);上界沒有兜底,填 999 與填 99 一模一樣;帶子查詢的 IN 與閾值無關,永遠展開成 11 行。

Q:為什麼同一條 SQL 兩次排出來行數差這麼多?

按三處排查:是不是同一檔、IN 閾值是不是同一個、其中一次是不是含子查詢。剩下的差異來自縮排——8 個空格與 Tab 之間,3 層巢套下最深前綴從 40 個字元掉到 5 個。

📎 相關文章

70 個位元組沒動,MIME 類型換了 13 次:副檔名才是唯一輸入 2026-10-23 轉 JPG 時透明被強制填底色:91 個品質檔位在 PNG 面前不參與編碼 2026-10-23 「全部清除」後還剩 553 位元組,「只去掉位置信息」連縮圖都沒動 2026-10-23 序號位數只是下限、重名時 (2) 插在後綴之前:批次重新命名 2000 個檔案的實測口徑 2026-10-23 兩條憑證指紋一條 95 個字元一條 64 個:差的 31 個全是冒號,摘要物件是整張 DER 2026-10-23