【问题标题】:How does one get the "next" record from database sorted by a specific attribute without loading all the records?如何在不加载所有记录的情况下从按特定属性排序的数据库中获取“下一个”记录?
【发布时间】:2017-02-10 03:35:39
【问题描述】:

情况如下: 我有一个事件模型,我想在视图中添加上一个/下一个按钮以获取下一个事件,但按事件 start 日期时间排序,而不是 ID/created_at

所以事件是按照开始的顺序创建的,所以我可以比较 ID 或获取下一个最高 ID 或类似的东西。例如。事件 ID 2 在事件 ID 3 之前开始。所以Event.next(3) 应该返回事件 ID 2。

起初我将开始日期时间作为参数传递并获取下一个参数,但是当有 2 个事件具有相同的开始时,这失败了。参数开始日期时间不包括微秒,所以会发生这样的事情:

order("start > ?",current_start).first

会一遍又一遍地返回相同的事件,因为current_start 不包括微秒,所以从技术上讲,当前事件将是> 而不是current_start 0.000000124 秒或类似的东西。

我为所有事情工作的方式是这样的:

module PrevNext
  extend ActiveSupport::Concern

  module ClassMethods
    def next(id)
      find_by(id: chron_ids[current_index(id)+1])
    end

    def prev(id)
      find_by(id: chron_ids[current_index(id)-1])
    end

    def chron_ids
      @chron_ids ||= order("#{order_by_attr} ASC").ids
    end

    def current_index(id)
      chron_ids.find_index(id)
    end

    def order_by_attr
      @order_by_attr ||= 'created_at'
    end
  end
end

型号:

class Event < ActiveRecord::Base

  ...

  include PrevNext

  def self.order_by_attr
    @order_by_attr ||= "start_datetime"
  end

  ...
end

我知道将所有 ID 放入一个数组中既糟糕又愚蠢*,但我不知道如何

  1. 按我想要的顺序获取记录列表
  2. 跳转到该列表中的特定记录(当前事件)
  3. 然后获取下一条记录

...全部在一个 ActiveRecord 查询中。 (使用带有 PostgreSQL 的 Rails 4)

*此表的记录可能永远不会超过 10k,因此它不是灾难性的糟糕和愚蠢。

我能做到的最好的办法就是按顺序只提取 ID,然后记住它们。

理想情况下,我想通过仅传递事件 ID 而不是开始日期参数来做到这一点,因为它是通过 GET 参数传递的,因此 URL 编码和解码越少越好。

必须有更好的方法来做到这一点。我也在 Reddit 上发布了它,但唯一建议的回复实际上并没有奏效。

感谢任何帮助或见解。谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby postgresql datetime


    【解决方案1】:

    您可以使用 SQL OFFSET keyword 获取下一个n 记录:

    china = Country.order(:population).first
    india = City.order(:population).offset(1).take
    # SELECT * FROM countries ORDER BY population LIMIT 1 OFFSET 1
    

    例如,通常如何进行分页:

    @countries = Country.order(:population).limit(50)
    @countries = scope.offset( params[:page].to_i * 50 ) if params[:page]
    

    另一种方法是使用query cursors。但是 ActiveRecord 不支持这一点,它构建一个普遍可重用的解决方案将是一项艰巨的任务,最终可能不是很有用。

    【讨论】:

      猜你喜欢
      • 2020-06-21
      • 2022-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多