【问题标题】:How to query records that are scoped to the most recent record per association如何查询范围为每个关联的最新记录的记录
【发布时间】:2019-07-31 18:32:17
【问题描述】:

我有一个 Mark 模型,其中有很多 mark_allocations

我需要找到正确的mark_allocations的数量,即

MarkAllocation.where(status: :correct).count

但是我只想查询每个标记最近的 mark_allocations。

我已经像这样在红宝石土地上实现了这一点:

MarkAllocation
  .order(created_at: :desc)
  .uniq(&:mark_id)
  .select { |m| m.correct? }
  .size

但这已成为性能瓶颈,我想在数据库级别进行选择。

到目前为止,在我的努力中,我可以毫无问题地获得每个标记的不同记录,但我正在努力申请订单以获取每个标记的最新记录。我也不知道如何从那一点开始,进一步查询正确的 mark_allocations。

我想出了这个:

MarkAllocation
  .select(:mark_id, :state, :created_at)
  .order(created_at: :desc)
  .distinct(:mark_id)
  .where(state: :correct)
  .count(:mark_id)

但我知道这是不正确的,我可以看到它输出的原始 sql 中缺少 ORDER 子句。

编辑:

这里是它当前如何使用 ruby​​ cody 的示例。

mark_allocations = [
  {mark_id: 1, status: :correct, created_at: 2.days.ago},
  {mark_id: 1, status: :incorrect, created_at: 1.day.ago},
  {mark_id: 2, status: :correct, created_at: 1.day.ago}
]

mark_allocations = mark_allocations.order(created_at: :desc).uniq(&:mark_id)
=> [  
  {mark_id: 1, status: :incorrect, created_at: 1.day.ago},
  {mark_id: 2, status: :correct, created_at: 1.day.ago}
]

mark_allocations = mark_allocations.select { |m| m.correct? }
=> [{mark_id: 2, status: :correct, created_at: 1.day.ago}]

mark_allocations.size
=> 1

【问题讨论】:

  • 那里有两个正确的mark_allocations,都对应于不同的mark_id,所以你会有两条记录,而不是一条。是您所说的“但是我只想从每个标记的最新 mark_allocations 中查询。”,如果您希望“每个标记”它们应该只是一个,您的论点是什么。
  • 较新的 mark_allocations 替换旧的 mark_allocations。 2天前创建的mark_allocation已经被一个不正确的mark_allocation替换了,所以当我查询正确的mark_allocation的数量时,我应该只用mark_id计算mark_allocation:2。如果你看看我的ruby解决方案。你可以看到我uniq 在选择正确的记录之前 记录。这意味着并非所有正确的都被计算在内。
  • 嗨,詹姆斯,像塞巴斯蒂安一样,我不确定我是否理解您为什么只在上面的数组中计算 mark_id: 2 才能得到 1。您之前的查询会得到唯一/不同的 mark_id,这将还是给你数二。如果您在另一列上使用 distinct ,我会理解它会给您一个……也许是关联或其他东西。请您澄清一下好吗?
  • 嗨@John,我已经编辑了我的问题,展示了 ruby​​ 代码如何工作的示例,希望可以澄清它。

标签: sql ruby-on-rails postgresql activerecord


【解决方案1】:

这将是您必须亲自使用一些 SQL 的情况之一。

在 PostgreSQL 中,您需要这样的查询来获取记录:

SELECT *
FROM (
         SELECT DISTINCT ON (mark_id) *
         FROM (
                  SELECT *
                  FROM marks
                  ORDER BY created_at DESC
              ) m
     ) mm
WHERE status = 1;

这是假设 1 是您正确状态的枚举整数。

或者,从这些记录中获取计数:

SELECT COUNT(*)
FROM (
         SELECT DISTINCT ON (mark_id) *
         FROM (
                  SELECT *
                  FROM marks
                  ORDER BY created_at DESC
              ) m
     ) mm
WHERE status = 1;

我会将其粘贴到我的 MarkAllocation 模型中,例如:

def self.counts
  query = <<-SQL.strip_heredoc
      SELECT COUNT(*)
      FROM (
               SELECT DISTINCT ON (mark_id) *
               FROM (
                        SELECT *
                        FROM marks
                        ORDER BY created_at DESC
                    ) m
           ) mm
      WHERE status = #{states[:correct]};
  SQL
  ActiveRecord::Base.connection.exec_query(query)
end

当然它看起来不漂亮,但它会更高效。

如果您想要实际的唯一记录,您可以执行以下操作:

def self.records
  nested_sql = MarkAllocation.all.order(created_at: :desc).to_sql
  query = <<-SQL.strip_heredoc
      SELECT *
      FROM (
               SELECT DISTINCT ON (mark_id) *
               FROM (
                        #{nested_sql}
                    ) m
           ) mm
      WHERE status = #{states[:correct]};
  SQL
  find_by_sql(query)
end

上面的记录示例展示了如何让 ActiveRecord 生成嵌套 SQL,以及如何使用 find_by_sql 将结果转换为 MarkAllocation ActiveRecord 结果。

【讨论】:

    猜你喜欢
    • 2022-12-18
    • 1970-01-01
    • 2022-10-01
    • 2020-11-19
    • 2010-09-23
    • 2020-07-29
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    相关资源
    最近更新 更多