2

我有一個表中的PostgreSQL 9.2數據庫,創建並填寫如下:優化窗函數的使用指數

CREATE TABLE foo(id integer, date date); 

INSERT INTO foo 
SELECT (id % 10) + 1, now() - (id % 50) * interval '1 day' 
FROM generate_series(1, 100000) AS id; 

現在,我需要找到所有對(id, date)使得日期是最大所有配對中的一個與id相同。查詢是一種衆所周知的,它通常使用所謂的ROW_NUMBER()

SELECT id, date 
FROM (
    SELECT id, date, ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) rn 
    FROM foo 
) sbt 
WHERE sbt.rn = 1; 

現在,我問該查詢的計劃,並計算出該WindowAgg節點需要一個表來先排序的窗函數。

Subquery Scan on sbt (cost=11116.32..14366.32 rows=500 width=8) (actual time=71.650..127.809 rows=10 loops=1) 
    Filter: (sbt.rn = 1) 
    Rows Removed by Filter: 99990 
    -> WindowAgg (cost=11116.32..13116.32 rows=100000 width=8) (actual time=71.644..122.476 rows=100000 loops=1) 
      -> Sort (cost=11116.32..11366.32 rows=100000 width=8) (actual time=71.637..92.081 rows=100000 loops=1) 
       Sort Key: foo.id, foo.date 
       Sort Method: external merge Disk: 1752kB 
       -> Seq Scan on foo (cost=0.00..1443.00 rows=100000 width=8) (actual time=0.006..6.138 rows=100000 loops=1) 

正如所料,分揀了多數人的查詢執行時間,它肯定會有助於使用index ES。

因此,我創建CREATE INDEX ON foo(id, date),並期望現在它將使用索引。但事實並非如此。我得到了與external merge相同的計劃,甚至關掉sequential scan也沒用。我剛剛結束了Bitmap Index Scan

-> Sort (cost=12745.58..12995.58 rows=100000 width=8) (actual time=69.247..90.003 rows=100000 loops=1) 
     Sort Key: foo.id, foo.date 
     Sort Method: external merge Disk: 1752kB 
     -> Bitmap Heap Scan on foo (cost=1629.26..3072.26 rows=100000 width=8) (actual time=5.359..12.639 rows=100000 loops=1) 
      -> Bitmap Index Scan on foo_id_date_idx (cost=0.00..1604.26 rows=100000 width=0) (actual time=5.299..5.299 rows=100000 loops=1) 

問題
可以WindowAgg用不完的指標進行排序?我認爲它不能不明白爲什麼... GroupAggreagte s可以和它提供良好的性能改善。

+0

你的排序發生在磁盤上。嘗試增加會話的'work_mem'並查看是否改善了事情 –

+0

@a_horse_with_no_name是的,我知道改善'work_mem'會將排序算法切換到快速排序,如果內存足夠用的話。但爲什麼我們不能使用'foo(id,date)'上的索引和窗口函數一起提供的排序呢?與聚合我們可以。 –

回答

4

要匹配你創建的索引:

CREATE INDEX ON foo(id, date) 

你將不得不做出這樣的:

ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC NULLS LAST)

這是ASC完美的順序相反。

這且不說,你可以只運行:

SELECT DISTINCT ON (id) 
     id, date 
FROM foo 
ORDER BY id, date DESC NULLS LAST; 

但是,這也許不是你想要問什麼。無論哪種方式,我就賺了指數:

CREATE INDEX ON foo(id, date DESC NULLS LAST) 

使max(date)id第一索引條目。 相關:

+0

不幸的是,它不適合我。你確定嗎?或者我做錯了什麼...? –

+0

雖然它適用於ASC。 –

+0

我的意思是'(PARTITION BY ID ORDER BY日期ASC NULLS LAST)' –

1

您可以重寫邏輯「的日期是同一個ID的所有對中最大的一個」直接翻譯:

SELECT id, date 
FROM (
    SELECT id, date, MAX(date) OVER (PARTITION BY id) as maxDate 
    FROM foo 
) sbt 
WHERE date = maxDate; 

這是不完全一樣的ROW_NUMBER,但RANK,它可能返回多行具有相同date