Rails查询具有最大列值的关联模型

时间:2017-05-10 17:17:50

标签: sql ruby-on-rails postgresql activerecord

我在rails中有3个模型:Author,Book和Page。页面属于书籍,书籍属于作者:

from pyspark.sql.functions import col

cols = [s for s in df.columns if "field" in s]
df.select(*(lookup_udf(col(c)).alias(c) if c in cols else c for c in df.columns)).show()
+---+------+------+------+-----+
| ID|field1|field2|field3|value|
+---+------+------+------+-----+
|  a|     y|     y|     x|    2|
|  b|     x|     x|     z|    3|
|  c|     z|     4|     z|    3|
+---+------+------+------+-----+

Page模型有一个名为page_number的列。我使用的是Postgres。

我的问题是:假设有一位作者class Author < ActiveRecord::Base has_many :books end class Book < ActiveRecord::Base belongs_to :author has_many :pages end class Page < ActiveRecord::Base belongs_to :book end ,如何查询作者的所有最后一页?换句话说,我想要该作者写的每本书的最后一页。我正在尝试下面的工作:

@author

修改

以下两行有效,但我希望了解更快/更清洁的解决方案:

Page.where(book_id: @author.books.pluck(:id)).select('MAX(page_number), *').group(:book_id)

1 个答案:

答案 0 :(得分:1)

最有效的方法可能是利用postgres' window functions

这样的查询不适合activerecord常见用例,因此您可能必须使用find_by_sql,但这可能是值得的。

在您的情况下,首先抓取图书ID可能是一个很好的电话,因为加入或额外的子查询可能不是有利的 - 您的电话。

假设您有@author.books.ids的图书ID列表。接下来我们想要的是一个“按”分组的页面列表,这样我们就可以为每个组挑选最后一页。设1,2为相关作者的书籍ID。

我们可以使用窗口函数和postgres中的rank函数来创建一个结果集,其中页面在书的分区(组)上排名。我们甚至会按页码对页面分区进行排序,以使最大页码(最后一页)位于每个分区的顶部。查询将如下所示:

select 
    *, 
    rank() over (
        partition by book_id order by page_number desc
    ) as reverse_page_index 
from pages 
where book_id in (1,2)

我们想象的pages结果集看起来像这样。

author 1, book 1, page 3, rank 1
author 1, book 1, page 2, rank 2
author 1, book 1, page 1, rank 3
author 1, book 2, page 6, rank 1
author 1, book 2, page 5, rank 2
author 1, book 2, page 4, rank 3
author 1, book 2, page 3, rank 4
author 1, book 2, page 2, rank 5
author 1, book 2, page 1, rank 6

页面记录按书籍分区,按页码升序排序,并在其分区中给出排名。

如果我们在之后只想要每本书的第一个排名(最后一页)进行窗口计算,我们可以像这样使用子选择:

select *
from
(
    select 
        *, 
        rank() over (
            partition by book_id order by page_number desc
        ) as reverse_page_index 
    from pages 
    where book_id in (1,2)
) as pages
where reverse_page_index = 1;

我们将上述想象结果集过滤为仅排名(reverse_page_index)为1(即最后一页)的页面记录。

现在我们的结果集将是:

author 1, book 1, page 3, rank 1
author 1, book 2, page 6, rank 1

您也可以按最后修改或任何需要订购此结果集。

find_by_sql中抛出该查询,您将拥有一些要使用的activerecord对象。