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 貴在每一列都要去另一張表配對。