2013-06-06 29 views
1

我有一個查詢,我認爲有一個相當常見的模式。考慮這張表:如何在執行分析查詢時避免DISTINCT作爲柺杖?

id | val | ts 
---+-----+------- 
a | 10 | 12:01 
a | 12 | 12:05 
a | 9 | 12:15 
b | 30 | 12:03 

我想通過每個id的時間戳獲取最新值。一些方法,你可以做到這一點:

-- where in aggregate subquery 
-- we avoid this because it's slow for our purposes 
select 
    id, val 
from t 
where (id, ts) in 
    (select 
    id, 
    max(ts) 
    from t 
    group by id); 

-- analytic ranking 
select 
    id, val 
from 
    (select 
    row_number() over (partition by id order by ts desc) as rank, 
    id, 
    val 
    from t) ranked 
where rank = 1; 

-- distincting analytic 
-- distinct effectively dedupes the rows that end up with same values 
select 
    distinct id, val 
from 
    (select 
    id, 
    first_value(val) over (partition by id order by ts desc) as val 
    from t) ranked; 

分析排名查詢感覺像是最容易提出一個有效的查詢計劃。但美學和維護方面,這是非常醜陋的(特別是當表格的方式不止一個值列時)。 在生產中的一些地方,當測試顯示性能相當時,我們使用獨特的分析查詢。

有什麼辦法可以做到像rank = 1這樣的事情,而不會結束這樣一個醜陋的查詢?

+0

如果有另外一行像a,10,13:45,那麼你期望得到哪個結果? (所以有記錄,id和val的組合不是唯一的)。 – Beryllium

+0

@Beryllium我提出的所有3個查詢應該按時間戳選取最新值。所以如果你的行被添加到示例表中,它應該處理得很好。 如果對於相同的值存在2個相同的時間戳,則會導致聚合查詢出現問題。 – kimbo305

+0

「柺杖」+1!獨特是最廣泛使用的kludge來消除重複,實際上查詢有問題。有合法的用途,但對我而言,每當我看到它時,它就是查詢中的紅旗。 – Bohemian

回答

1

如果你被id分組僅

select 
    id, max(ts) 
    from x 
    group by id 
    order by id 

,如果組由idval

select 
    id, val, max(ts) 
    from 
    x 
    group by id, val 
    order by id, val 

,所以我不會用把聚集在一個子查詢(可能是更慢) 我不會使用窗口aggregregate函數(因爲你可以使用普通的group bymax) ,我不會使用distinct,因爲這意味着不同的東西(至少對我而言)。

如果小組id,和你想的val值的一個,我建議使用窗口集合函數,因爲你必須以某種定義哪些val選擇:而這個意向屬於進order bypartition by之後。

從維護的角度來看,我認爲窗口聚合函數真正描述了你的意圖 - 你想達到的目標。其他查詢以某種方式隱藏了他們的意圖。就我個人而言,當我閱讀你的查詢時,第二個是最容易理解的。

從性能角度來看,我可以確認窗口聚合速度很快(至少在我的情況下)。這可能是優化器也從語法中受益。

1

這是最簡單和最快的:

select distinct on (id) 
    id, ts, val 
from t 
order by id, ts desc 

distinc on(PostgreSQL只)將只返回一個排的每個ID。用order by你可以控制哪一個。在這種情況下,最後的ts。使用distinct on,您可以在結果集中包含儘可能多的列,而無需中間步驟。 distinct on中使用的列必須首先包含在order by中。