Postgresql

如何在過濾3個條件時涉及表索引?

  • June 14, 2018

我有一個包含 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 永遠不是負數的情況下這樣做,否則選擇其他一些虛擬函式來使用。

引用自:https://dba.stackexchange.com/questions/209521