2012-04-13 41 views
2

我有一個author_id字段的書籍表。獲取按uniqe字段值分組的最新行

我想獲取一組書籍,其中只包含每個作者的一本書。最新的updated_at字段。

在Postgres上使用直接方法(如Books.all.group('author_id'))的問題是它需要GROUP BY塊中的所有請求字段。 (請參閱https://stackoverflow.com/a/6106195/1245302

但我需要爲每個作者獲取所有Book對象,最近的一個忽略所有其他字段。 在我看來,有足夠的數據讓DBMS準確找到我想要的行,至少我可以在GROUP BY塊中自己沒有任何其他字段。 :)

是否有任何簡單的Rails 3 + Postgres(版本< 9)或SQL實現 獨立的方式來獲取?

UPDATE Postgres的很好的解決方案:

books.unscoped.select('DISTINCT ON(author_id) *').order('author_id').order('updated_at DESC') 

BUT!仍然存在問題 - 結果首先排序爲author_id,但我需要在相同的author_id -s內找出排序方式updated_at(發現前10位最近的書籍作者)。

和Postgres不允許你改變的ORDER BY參數順序DISTINCT查詢:(

+0

說起_array_ - 是它是PostgreSQL還是你正在尋找的ruby數組? – vyegorov 2012-04-13 19:35:46

+0

我只需要Rails模型對象的數組,PostgreSQL 8.4 – aristofun 2012-04-14 16:15:31

回答

1

我不知道Rails的,但希望你展示你想要將有助於讓你的方式是什麼SQL生成正確的SQL

SELECT DISTINCT ON (author_id) * 
    FROM Books 
    ORDER BY author_id, updated_at DESC; 

DISTINCT ON (author_id)部分不應與結果列清單的一部分混淆 - 它只是說,將有每一個AUTHOR_ID行的DISTINCT ON條款清單必須是。條款i的主要部分ORDER BY n這樣的查詢,並且保留的行是基於ORDER BY子句的其餘首先排序的那一行。

對於大量的這種寫作方式,查詢通常比基於GROUP BY或窗口函數的任何解決方案(通常爲一個數量級或更多)快得多。不過,這是一個PostgreSQL擴展;所以它不應該用在可移植的代碼中。

如果您想在另一個查詢中使用此結果集(例如,查找最近更新的10個作者),有兩種方法可以做到這一點。您可以使用子查詢,像這樣:

SELECT * 
    FROM (SELECT DISTINCT ON (author_id) * 
      FROM Books 
      ORDER BY author_id, updated_at DESC) w 
    ORDER BY updated_at DESC 
    LIMIT 10; 

你也可以使用一個CTE,像這樣:

WITH w AS (
    SELECT DISTINCT ON (author_id) * 
    FROM Books 
    ORDER BY author_id, updated_at DESC) 
SELECT * FROM w 
    ORDER BY updated_at DESC 
    LIMIT 10; 

約熱膨脹係數通常建議持有在這裏:使用它們只有在沒有其他寫入查詢的方式,或者如果需要通過引入優化障礙來強制規劃者。計劃非常相似,但通過CTE掃描傳遞中間結果會增加一些開銷。在我的小測試集中,CTE表格慢了17%。

+0

有沒有什麼方法可以在Rails中生成上面的代碼? – kgrittn 2012-04-14 13:16:58

+0

非常感謝,在Rails中生成它的方式我發現: 'books.unscoped.select('DISTINCT ON(author_id)*')。order('author_id')。order('updated_at DESC')' – aristofun 2012-04-15 08:51:36

+1

could你幫我訂購問題嗎? – aristofun 2012-04-15 09:00:02

0

這是馬後炮,但在應對有關覆蓋/重置默認順序問題,使用.reorder(nil).order(:whatever_you_want_instead)

(我不能評論,所以張貼至於現在的答案)