Rails 3 对子查询应用 limit 和 offset

4

我有一个查询,大致如下(在song.rb中):

def self.new_songs
  Song.where(id: Song.grouped_order_published).select_important_stuff
end

在我的应用程序中,稍后在控制器中传递了限制和偏移量:

@songs = Song.new_songs.limit(10).offset(10)

我的应用程序结构需要保持这种设置方式,但不幸的是,它非常缓慢,因为它限制了外部查询而不是子查询。是否有一种方法可以公开子查询,使其接收限制和偏移量,而不是外部查询?
编辑:我应该补充说明我正在使用postgres 9.2。
编辑2:我希望以这种方式进行分页,并且需要获取总行数的“计数”。因此,我会像这样做:
@songs = Song.new_songs
...
@pages = @songs.count / 10
...
render @songs.limit(params[:page]).offset(0)

如果我想要对它进行修改,我必须完全重新做一遍(这在很多地方都是如此)。如果不限制它直到实际调用,我可以在中间进行计数,然后最终只获取页面。我想我更需要的是有关如何使用内部查询完成此操作的建议,而不会随着数据库增长变得非常缓慢。

所以我通过不使用子查询来解决了这个问题。不过,解决这个问题仍然是一个有趣的挑战。 - Nathan Wienert
所以问题是你需要知道匹配总数并获取你的10个结果,但你不想运行两次查询,对吧?当然,你也不想改动更多的代码。 - mu is too short
2个回答

3

我无法尝试此解决方案,也不是 Ruby 专家,但据我理解,您需要一个对象将所有方法调用传递到完整查询中,但在此过程中限制并存储有限的子查询。

它可能看起来像这样:

class LimitedSubquery < Object

  # sub_query has to be stored so we can limit/offset it
  def initialize(sub_query)
    @sub_query = sub_query
  end

  # Make sure everybody knows we can be used like a query
  def self.respond_to?(symbol, include_private=false)
     super || full_query.respond_to?(symbol, include_private)
  end

  # Missing methods are probably meant to be called on the whole query
  def self.method_missing(method_sym, *arguments, &block)
    if full_query.respond_to?(method_sym)
      full_query.send(method_sym, *arguments, &block)
    else
      super
    end
  end

  # Generate the query for execution
  def self.full_query
    Song.where(id: @sub_query).select_important_stuff
  end

  # Apply limit to sub_query
  def self.limit(*number)
    LimitedSubquery.new(@sub_query.limit(*number))
  end

  # Apply offset to sub_query
  def self.offset(*number)
    LimitedSubquery.new(@sub_query.offset(*number))
  end
end

然后像这样调用它
def new_songs
  LimitedSubquery.new(Song.grouped_order_published)
end

如果我有任何错误,请帮我进行修改!

谢谢!

TC


1
没有检查过,但它的逻辑是正确的。我最终找到了一种避免子查询的方法,但你应该得到这些分数。 - Nathan Wienert
谢谢!原始查询似乎可以重写,但正如您在评论中所述,您想要解决原始问题的解决方案。 - TheConstructor

0
你应该考虑使用will_paginate gem。这样可以避免手动计算所有内容的麻烦。;-)

我不理解你的评论。就我所理解的,你正在寻找分页。如果不是,请澄清你的问题。谢谢。另一种方法是给new_songs方法两个参数limit和offset,然后将它们用于def self.new_songs(limit, offset) Song.where(id: Song.grouped_order_published.limit(limit).offset(offset)).select_important_stuffend调用@songs = Song.new_songs.limit(10).offset(10)根本无法工作。 - awenkhh
我认为已经足够清楚了。如果您想看到一个真正回答我的问题的答案,请查看所选的答案。 - Nathan Wienert
很高兴你现在找到了解决问题的方法! - awenkhh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接