統計資訊是引擎另外去數出來、存在一旁的一份摘要:每張表大概多少列、某個欄位的值大概怎麼分布。它不在查詢的文字裡,也不在資料表裡——資料本身有多少列是一回事,引擎「知道」有多少列是另一回事,而 query optimizer 選 query plan 時讀的是後者。

誰產生它,什麼時候

多數引擎用一道明確的指令收集:SQLite 與 PostgreSQL 都是 ANALYZE,PostgreSQL 另有 autovacuum 在背景定期重收。剛建好的資料庫沒有統計,剛大量寫入之後統計是舊的。

沒有統計的時候引擎不會拒絕執行,它退回去用別的線索。SQLite 在這種情形下照查詢文字裡表出現的先後決定計畫——書寫順序在這裡才有影響力,而那是引擎沒有別的資訊時的退路,不是它的依據。跑過 ANALYZE 之後同樣兩種寫法得到同一個計畫。

它決定的是估計,不是事實

最佳化器比較的是估計代價,而估計來自這份摘要。所以判斷品質取決於統計有多新,與查詢寫得多好無關:

  • 查詢沒動而某天變慢,變的通常是統計而不是寫法——歸因的起點放在統計的新舊
  • 計畫上的估計列數與實際列數差很多,先懷疑統計,再懷疑寫法
  • 在小資料上量到的計畫套到 production 常常對不上,因為兩邊的統計不同

它與索引是兩件事

索引決定引擎有沒有一條快的存取路徑可以走,統計決定它知不知道該走那一條。索引建了而統計沒更新的時候,最佳化器可能估錯而不走它——兩者要一起看。

概念位置

統計資訊是「查詢的文字不足以決定執行方式」這件事的具體內容。查詢說要什麼結果,query optimizer 決定怎麼拿,而它決定的依據既不在查詢裡也不在資料裡——它在這一份另外存著的摘要上。

這讓同一段 SQL 沒有固定的執行方式:資料沒變、索引沒變、查詢沒變,只要統計重收過一次,query plan 就可能不同。所以「這段查詢的計畫是什麼」這個問題要連同資料庫的狀態一起問。

往下走

有無統計的兩個計畫並排、以及書寫順序為什麼只是退路,在 SQL.1 宣告式的紅利與代價。統計、索引與資料分布三者各自決定代價的哪一部分,在 SQL.17 查詢的代價。