估計和實際差很多,代表資料庫對這張表的認識壞了

執行計畫裡每個節點都印兩個 rows:前面那個是估計,括號裡 actual 那個是事實。 兩者差很多的時候,那個差距本身就是最重要的訊號——比計畫選了什麼做法還重要。

Seq Scan on orders  (cost=0.00..18240.00 rows=1 width=240)
                    (actual time=0.031..412.775 rows=87432 loops=1)
  Filter: (status = 'pending')
  Rows Removed by Filter: 712568

估計 1 筆,實際 87432 筆。差了 8 萬倍。

為什麼這比「它選了全表掃描」更要緊

資料庫是在查詢真正開始跑「之前」決定要用哪種做法的。 還沒跑,它不可能知道會有幾筆——它是用猜的,依據是先前收集過的、關於這張表的資料分布摘要。

這張 PostgreSQL 的處理流程圖把機制畫得很清楚:Planner(③)和 Executor(④)是分開的兩步,而中間那條 Look up Statistics 的箭頭只餵給 Planner。Executor 開跑的時候,計畫早就定好了。

圖片來源是 Medium 的 CDN 轉貼,原圖看起來出自《The Internals of PostgreSQL》。

那個 rows=1 就是它的猜測。所以:

它整個判斷,建立在一個錯了 8 萬倍的數字上。

這份輸出真正在說的不是「Seq Scan 不好」,而是 這個資料庫對這張表的認識已經壞掉了。它現在挑的做法可能對也可能錯,而你沒辦法從這份輸出判斷,因為它的依據本身就是壞的。

所以順序是固定的

1. 先修猜測        → ANALYZE(或確認 autovacuum 是健康的)
2. 再看計畫變了沒
3. 到這時候,「要不要加索引」這個問題才有辦法回答

在修好之前就加索引,三種結果都有可能,而你分辨不出是哪一種:

它以為只有 1 筆 → 用了索引實際 8 萬筆,每筆都要回表撈,可能比全表掃還慢
修好後它說「8 萬筆佔 11%,太多了」它根本不會用那個索引,白加,還要付寫入的代價(見 索引是拿寫入換查詢,寫多讀少的表加索引是純虧)
修好後時間就掉下來了根本不需要加索引

順帶一提

Rows Removed by Filter: 712568 是 WHERE 決定回傳幾列,索引才決定要看過幾列 的實測證據——讀了 80 萬列、留下 8 萬列,兩個量在輸出裡就是分開印的。