我有一张带有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
个参数的顺序:(
答案 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)
(我无法发表评论,所以现在发布作为答案)