複合索引的欄位 = Index Cond 的欄位 + Filter 裡的等值欄位

診斷的核心邏輯很單純:看它在 Filter 浪費了什麼,就把那些欄位補進索引。

三步 SOP

[步驟 1] 找耗時大戶
  計算 (actual time 的第二個數字) × loops,找出哪一行吃掉 80% 以上的時間

[步驟 2] 看該行有沒有大量丟棄
  檢查 Rows Removed by Filter,數量達數千到數十萬就是它

[步驟 3] 組合索引欄位
  索引欄位 = [Index Cond 的欄位] + [Filter 裡的等值比對 (=) 欄位]

Index Cond 和 Filter 的分界見 Index Cond 是在索引上定位,Filter 是撈出來再逐筆比;步驟 1 的乘法見 loops 大於 1 時,actual time 和 rows 是每次迴圈的平均。

案例一:索引只涵蓋了第一個條件

->  Bitmap Heap Scan on pg060 s
      Filter: ((a.div_no = (div_no)::bpchar) AND (a.emp_org_id = (emp_org_id)::bpchar))
      Rows Removed by Filter: 38118
      ->  Bitmap Index Scan on pg060_idx1
            Index Cond: (vrfy_wkym = a.vrfy_wkym)
Index Cond 的欄位  →  vrfy_wkym
Filter 的等值欄位  →  div_no, emp_org_id
CREATE INDEX idx_pg060_lookup ON pg060 (vrfy_wkym, div_no, emp_org_id);

B-Tree 就能一口氣定位到 0 筆,根本不用去掃那 3.8 萬筆。

案例二:用了主鍵,但主鍵的後續欄位對不上

->  Index Scan using pg059_pk on pg059 b
      Index Cond: (vrfy_wkym = a.vrfy_wkym)
      Filter: ((a.div_no = (div_no)::bpchar) AND (a.emp_org_id = (unit_org_id)::bpchar))
      Rows Removed by Filter: 33968

「用了主鍵」不代表沒事。 這裡 Index Cond 只有 vrfy_wkym——代表主鍵的第一個欄位剛好是它,但後續欄位命不中這條 SQL 的查詢條件(主鍵可能是 (vrfy_wkym, serial_no) 之類)。

CREATE INDEX idx_pg059_lookup ON pg059 (vrfy_wkym, div_no, unit_org_id);

判準不是「這張表有沒有索引」,是「Index Cond 裡出現了幾個你的查詢條件」。 只出現一個,剩下的都在 Filter 裡。

成效

兩張表從「掃 3 萬多筆再過濾」變成「精準命中 0~1 筆」,原本 14 秒縮到幾毫秒。

但要先確認兩件事

只有**等值比對(=)**的欄位適合往複合索引裡疊;範圍條件擺在最後一個位置之後,再往後的欄位就用不到了。