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 是每次迴圈的平均。