Nested Loop 的執行順序是上外下內,不是最深的先跑
縮排會讓人以為「最深的先跑」。對 Nested Loop 而言那是錯的——決定順序的不是深度,是兄弟關係。
結構規則
一個 Join 節點底下一定剛好有「兩個」縮排深一層的子節點:上面那個是外層輸入(驅動),下面那個是內層輸入(被驅動)。
[Level 0] Nested Loop Left Join ← 總耗時 14.3 秒
│
├── [子 1・左/外] Nested Loop Left Join ← 7.4 秒
│ ├── [孫 1-1・左/外] Index Scan on dtapb001 a
│ └── [孫 1-2・右/內] Bitmap Heap Scan on pg060 s
│ └── Bitmap Index Scan on pg060_idx1
│
└── [子 2・右/內] Index Scan on pg059 b
為什麼最深的那個反而不能先跑
看 pg060_idx1 的搜尋條件:
Index Cond: (vrfy_wkym = a.vrfy_wkym)
裡面有 a.vrfy_wkym——a 就是 dtapb001 a。
如果
dtapb001 a還沒跑,資料庫怎麼可能知道a.vrfy_wkym是多少?
它根本不知道要找什麼。 換成程式碼就很明顯:
# 不能在還沒取得 a 之前,就先執行迴圈內部
for a in dtapb001: # 必須先拿到 a
for s in pg060:
if s.vrfy_wkym == a.vrfy_wkym: # 依賴 a 的值
...所以 dtapb001 a 必須最先發動——它只依賴靜態常數(emp_id = 'JXXX'),不依賴任何其他表。拿到它的資料之後,才把值傳給內層去查索引。
這也是為什麼內層節點的 loops 會等於外層的列數(這裡外層 15 筆 → 內層 loops=15)。
巢狀的 Join 怎麼讀
外層 Join 的「外層輸入」可以是另一個 Join 的結果:
第一步:裡面那個 Nested Loop
左 = dtapb001 a(15 筆)
右 = pg060 s(每筆查一次,共 15 次)
→ 產生中間結果集(15 筆)
第二步:最外面那個 Nested Loop
左 = 第一步的結果(15 筆) ← 不是某張表,是上一個 Join 的產物
右 = pg059 b(拿這 15 筆各查一次,loops=15)
→ 輸出最終 15 筆
口訣
兩兩成對,上外下內;先跑上方驅動表,再帶入下方被查表。
用法:找到 Nested Loop → 看它直屬的兩個子節點,上面的先跑、下面的後跑 → 如果上面那個自己又是 Nested Loop,就繼續鑽進去拆。
計畫整體的層級怎麼看見 執行計畫是一棵樹,從下往上讀;內層節點的耗時怎麼算見 loops 大於 1 時,actual time 和 rows 是每次迴圈的平均。