loops 大於 1 時,actual time 和 rows 是每次迴圈的平均

單表查詢的 loops=1,看到什麼就是什麼。但巢狀計畫裡的子節點會被跑很多次,那時候印出來的數字是「每一次的平均」,不是總量。

Nested Loop Left Join  (actual time=561.213..14329.521 rows=15 loops=1)
   ->  Nested Loop Left Join  (actual time=558.920..7446.517 rows=15 loops=1)
         ->  Index Scan using dtapb001_pk on dtapb001 a  (actual ... rows=15 loops=1)
         ->  Bitmap Heap Scan on pg060 s  (actual time=496.333..496.333 rows=0 loops=15)
                                                                          ^^^^^^^^^
   ->  Index Scan using pg059_pk on pg059 b  (actual time=31.014..458.859 rows=1 loops=15)
                                                                            ^^^^^^^^^

底下那兩個節點看起來只花幾百毫秒、而且一個只回 0 列、一個只回 1 列,但它們各被跑了 15 次:

pg060 s  →  496.333 ms × 15 ≈ 7,445 ms   (約 7.4 秒)
pg059 b  →  458.859 ms × 15 ≈ 6,883 ms   (約 6.8 秒)
                              ─────────
                              14,328 ms   ← 正好是根節點的 14,329 ms

整條 SQL 的 14.3 秒全部在這兩行,但它們自己印出來的數字是 496 和 458。

乘回去之後才看得到真正的工作量:pg060 每次丟棄 38,118 列,15 次就是 57 萬次比對。

判準

看到 loops=N 而 N 不是 1,就把 actual time 和 rows 都乘以 N,再判斷它貴不貴。

不乘回去的話,巢狀迴圈底下的節點永遠看起來很便宜——這是讀執行計畫最常見的誤判。

層級怎麼看見 執行計畫是一棵樹,從下往上讀;為什麼巢狀迴圈的成本會這樣放大,見 JOIN 貴在每一列都要去另一張表配對。