【问题标题】:Activerecord sort by column and then created_at?Activerecord 按列排序然后created_at?
【发布时间】:2016-05-10 10:40:22
【问题描述】:

所以我有一个项目表。每个项目都有一个状态,可以是“每日”、“每月”、“每年”或“未完成”。

我要做的是创建一个单独的 Activerecord(或 SQL)查询,该查询首先(按 created_at)排列未完成的项目,然后按其 created_at 日期排列其余项目(无论其状态如何),同时限制返回的项目总数为15。

例如,如果我有 30 个未完成项目和 30 个年度项目,则查询返回 15 个未完成项目(按其 created_at)。

如果我有 10 个未完成项目和 30 个年度项目,它会返回这 10 个未完成项目(按 created_at),然后返回 5 个年度项目(按 created_at) - 返回的未完成项目应位于返回数组的开头。

如果我没有任何未完成的项目和 30 个年度项目,它将在 created_at 日期返回 15 个年度项目。

感谢您的帮助!

【问题讨论】:

  • 为什么是单个查询?最坏情况下的 2 个查询就可以解决问题。最佳情况触发 1 个查询。
  • 我认为这个单一的查询是不可能使用 ORM 的
  • 单次查询的原因是我在实现无限滚动,需要保留请求的页码偏移量。可以使用两个不同的偏移量,但这看起来很笨拙。

标签: sql ruby-on-rails activerecord


【解决方案1】:

首先,只需定义一个这样的范围:

class Item < ActiveRecord::Base
  scope :ordered, -> {
    joins("LEFT OUTER JOIN ( SELECT id, created_at, status
                             FROM items
                             WHERE items.status = 'outstanding'
                           ) AS temp ON temp.id = items.id AND temp.status = items.status"
         ).order('temp.created_at NULLS LAST, items.status, items.created_at')
    }
end

神奇的是:(希望你的表名是items

  • 左加入itemsoutstanding items。所以 temp.idtemp.created_at 将是 NULL 对于没有状态 outstanding 的项目
  • 首先按temp.created_at NULLS LAST 排序,因此没有状态outstandingitems 将最后排序。然后按正常顺序进行:items.status(这使得相同的状态将被彼此关闭)和items.created_at

您可以使用范围 ordered 仅针对 15 个项目运行查询:

Item.ordered.limit(15)

【讨论】:

  • 即使它可能是 OP 要求的,并返回正确的结果,我不希望在我工作的应用程序中看到这样的代码,因为它不可读 + 它可以通过 AR ORM 实现.我绝不说它不好,只是一个观点——毕竟,Ruby 是关于可读性的。最坏的情况是有查询对象模式,你可以把它放在那里,但仍然......
  • @AndreyDeineko:同意,可读性很重要,但对我来说,编程不是惯例,一切都需要打破才能看到更好的东西。我仍然是一名学习者,而不是专业的开发人员,我认为:)。我明白了你的想法,谢谢!
  • 专业是主观的东西,但有自己的经验绝对是有价值的。嘿,被接受是你的答案,所以.. ;)
【解决方案2】:

不确定单个查询,但这是一个足够的解决方案:

scope :ordered,     -> { order(:created_at) }
scope :outstanding, -> { where(status: :outstanding).ordered }

def self.collection
  return ordered.limit(15) if outstanding.count.zero?
  return outstanding.limit(15) if outstanding.count >= 15
  outstanding + ordered.limit(15 - outstanding.count)
end

Item.collection # will return the array of your records

【讨论】:

    【解决方案3】:

    虽然在最坏的情况下它不是一个查询(2 个查询),但它解决了您的问题:

    items = Item.where(:status => "outstanding").order('created_at DESC').limit(15)
    items = (items.size == 15) ? items : items + Item.where('status != ?', "outstanding").order('created_at DESC').limit(15-items.size)
    

    【讨论】:

    • 我想他想要那个。根据问题“query that arranges the outstanding items first (by created_at) and then the rest of the items (regardless of their status) by their created_at date
    猜你喜欢
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    • 2017-04-05
    • 2010-12-14
    • 1970-01-01
    • 2023-03-05
    相关资源
    最近更新 更多