Postgresql
如何在過濾3個條件時涉及表索引?
我有一個包含 3000 萬條記錄的表,我想通過一些查詢非常快地獲取數據。我有這些欄位:id(int),field1(int),field2(int),field3(int)。所以我想按這個條件查詢數據
SELECT * FROM mytable WHERE field3 = 1 AND (field1 < field2 OR field2 < 0) LIMIT 1
我得到的查詢計劃
explain (analyze, buffers) select * from mytable mt where (mt.field1 < mt.field2 OR mt.field2 < 0) and mt.field3 = 1 limit 1; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------- Limit (cost=0.00..0.37 rows=1 width=13) (actual time=525.050..525.051 rows=1 loops=1) Buffers: shared hit=245 read=35166 -> Seq Scan on mytable mt (cost=0.00..359373.97 rows=983846 width=13) (actual time=525.048..525.048 rows=1 loops=1) Filter: ((field3 = 1) AND ((field1 < field2) OR (field2 < 0))) Rows Removed by Filter: 4006175 Buffers: shared hit=245 read=35166 Planning time: 0.163 ms Execution time: 525.077 ms
我在 field3 上設置了索引,但它僅由 postgres 使用
SELECT * FROM mytable WHERE field3 = 1 LIMIT 1
我嘗試在欄位(field3,field1,field2)上設置索引,然後在(field3,field2,field1)上設置索引,但它只能在沒有
field1 < field2
.然後我嘗試僅為條件設置索引
field1 < field2
。索引 (field1, field2) 和 (field2, field1) 也不起作用。我需要什麼索引才能在條件下使用它
field1 < field2
?更新。我按條件添加部分索引
CREATE INDEX idx_mytable_condition_1 ON mytable (field3) WHERE (field1 < field2 OR field2 < 0);
但無論如何沒有使用索引,只有 seq 掃描
explain (analyze, buffers) select * from mytable mt where mt.field3 = 1 and (mt.field1 < mt.field2 OR mt.field2 < 0) limit 1; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------- Limit (cost=0.00..0.37 rows=1 width=37) (actual time=492.787..492.787 rows=1 loops=1) Buffers: shared hit=277 read=35134 -> Seq Scan on mytable mt (cost=0.00..359373.97 rows=983846 width=37) (actual time=492.785..492.785 rows=1 loops=1) Filter: ((field3 = 1) AND ((field1 < field2) OR (field2 < 0))) Rows Removed by Filter: 4006175 Buffers: shared hit=277 read=35134 Planning time: 0.110 ms Execution time: 492.806 ms
您對錶達式的相關性有疑問。
field3 = 1
有選擇性地耗盡的情況((field1 < field2) OR (field2 < 0))
。PostgreSQL 認為這些是獨立的,但事實並非如此。所以它認為你會通過 seq 掃描很快找到符合條件的行。但相反,它必須探勘 400 萬個才能找到第一個。PostgreSQL 的一個怪癖是它收集有關功能索引的特殊統計資訊,但不收集普通部分索引的統計資訊。因此,如果您在部分索引中添加一個虛擬函式,您可能會得到一個更好的計劃:
create index on mytable (abs(field3)) WHERE (field1 < field2 OR field2 < 0);
然後您的查詢必須
abs(field3)=1
在條件中使用。當然你只能在 field3 永遠不是負數的情況下這樣做,否則選擇其他一些虛擬函式來使用。