2012-04-13 73 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)

(我不能评论,所以张贴至于现在的答案)