2017-09-24 138 views
1

模式:爲什麼PostgreSQL沒有正確使用索引?

create table records(
    id   varchar, 
    updated_at bigint 
); 
create index index1 on records (updated_at, id); 

查詢。它迭代最近更新的記錄。獲取10條記錄,記得最後一條,然後取下10條記錄等等。

select * from objects 
where updated_at > '1' or (updated_at = '1' and id > 'some-id') 
order by updated_at, id 
limit 10; 

它採用了指數,但它並不明智地使用它,也適用於過濾和處理萬噸的記錄,請參閱下面的查詢說明Rows Removed by Filter: 31575

奇怪的是,如果您刪除or並保留左側或右側狀態 - 對兩者都適用。但似乎如果不能找出如何正確應用索引,如果這兩個條件與or同時使用。

Limit (cost=0.42..19.03 rows=20 width=1336) (actual time=542.475..542.501 rows=20 loops=1) 
    -> Index Scan using index1 on records (cost=0.42..426791.29 rows=458760 width=1336) (actual time=542.473..542.494 rows=20 loops=1) 
     Filter: ((updated_at > '1'::bigint) OR ((updated_at = '1'::bigint) AND ((id)::text > 'some-id'::text))) 
     Rows Removed by Filter: 31575 
Planning time: 0.180 ms 
Execution time: 542.532 ms 
(6 rows) 

Postgres的版本是9.6

+0

'...其中的updated_at> '1' ...'你不應該引用整數常量。 – wildplasser

+0

@wildplasser我試過沒有引號,同樣的事情。 –

+0

'width = 1336'這是一個*非常寬的表, – wildplasser

回答

2

我會嘗試這是兩個單獨的查詢,其結果結合這樣的:

select * 
from 
    (
    select * 
    from  objects 
    where updated_at > 1 
    order by updated_at, id 
    limit 10 
    union all 
    select * 
    from  objects 
    where updated_at = 1 
     and id > 'some-id' 
    order by updated_at, id 
    limit 10 
) t 
order by updated_at, id 
limit 10 

我的猜測是,這兩個查詢將每個優化非常好,兩者運行都會比現在更有效率。

如果可能的話,我也會讓這些列不爲NULL。

+0

是的,我也想過。但我認爲PostgreSQL足夠聰明,也許在我的代碼中有一些錯誤... –

+0

是的,它解決了這個問題,謝謝。奇怪...我希望PostgreSQL更好... –

2

PostgreSQL對索引的調用有一個優化。

例如,給定的索引(A,B,C),並且其中一個 = 5和B> = 42和c < 77,該指數將必須從第一條目掃描的查詢條件其中a = 5和b = 42直到 = 5的最後一個條目。具有c> = 77的索引條目將被跳過,但仍然必須被掃描。該索引原則上可以用於對b和/或c有約束的查詢,但對整個索引將不得不進行掃描,因此在大多數情況下,規劃器會優先使用順序表掃描使用索引。

https://www.postgresql.org/docs/9.6/static/indexes-multicolumn.html

相關問題