获取按唯一字段值分组的最新行

时间:2012-04-13 18:49:55

标签: ruby-on-rails postgresql activerecord

我有一张带有author_id字段的图书表。

我想获取一系列书籍,其中只包含每本作者的一本书。具有最新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实现 这是独立的方式吗?

更新 Postgres的绝佳解决方案:

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

BUT!还有问题仍然存在 - 结果首先按author_id排序,但我需要在同一updated_at - s内按author_id排序(以查找最近的前10名)书作者)。

Postgres不允许您更改ORDER BY个查询中DISTINCT个参数的顺序:(

2 个答案:

答案 0 :(得分: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子句中的列表必须是此类查询中ORDER BY子句的前导部分,并且保留的行是基于 rest 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扫描传递中间结果会增加一些开销。在我的小测试集中,CTE形式慢了17%。

答案 1 :(得分:0)

这是迟来的,但在回答有关覆盖/重置默认订单的问题时,请使用.reorder(nil).order(:whatever_you_want_instead)

(我无法发表评论,所以现在发布作为答案)