【问题标题】:Find the most recently answer for a given group of questions查找给定问题组的最新答案
【发布时间】:2021-07-04 16:27:10
【问题描述】:

我的申请有注册问答的概念

class RegistrationQuestion < ApplicationRecord
  has_many :registration_answers, dependent: :destroy

  # RegistrationQuestions that are 'original' questions are the questions
  # configured to join as a full member, they have a null original_id field.

  # However we also support 'one time events'. When created those events get a copy of the full
  # registration questions so that users can have different questions for one time events compared to full membership

  belongs_to :original, class_name: "RegistrationQuestion", optional: true
  has_many :derived_questions, class_name: "RegistrationQuestion", foreign_key: 'original_id'
end

为了给回访用户提供建议的答案,我想检索任何相关问题的最新答案。这就是我被绊倒的地方:

class RegistrationAnswer < ApplicationRecord
  belongs_to :registration_question

  # Simplified. I can get the related question ids, need help getting distinct answers
  def self.related_answers_for(profile, question_ids)
    recent_related_answer_ids = RegistrationAnswer.joins(:registration_question)
                                                  .where(profile: profile, registration_question_id: related_question_ids)
                                                  .order(updated_at: :desc)
                                                  .select('registration_questions.original_id', :updated_at, :id)
                                                  .group('registration_questions.original_id')
                                                  .map { |ra| ra.id }

    RegistrationAnswer.where(id: related_answer_ids)
  end
end

我正在尝试获取任何相关注册问题的最新答案。相关注册问题是具有相同 original_id 集的问题。

但是这个 sql 抛出一个“PG::GroupingError: ERROR”告诉我我需要把 updated_at 和 id 都放在 group 子句中。但是,当我添加这两个时,它不再按我的意愿对它们进行分组,因为它根据注册答案 ID 返回不同的组。我真正想要的是最新的答案,仅按registration_question.original id分组。

【问题讨论】:

  • 改变分组争论的顺序会改变结果的分组吗?
  • 不,无论组参数顺序如何,顺序似乎都保持不变
  • 你可以试试窗口函数。我不是他们的专家,但你可以用一个来做你需要的事情
  • 我不知道 activerecord,但如果我正在编写查询来执行此操作,我会使用 postgres 特定语法 DISTINCT ON 而不是 group by。
  • 这应该很容易优化给定的实际 Postgres 表定义(CREATE TABLE 语句)和基数。但我没有看穿混淆层。基本说明:stackoverflow.com/a/7630564/939860

标签: ruby-on-rails postgresql activerecord


【解决方案1】:

由于您使用的是 Postgres DB,因此您可以使用 ROW_NUM() https://www.postgresqltutorial.com/postgresql-row_number/ 您根据 original_id 和 updated_at 顺序对行进行编号,然后您将仅查询第一条记录 (most_recent),即 row_number = 1 的行

您可以使用find_by_sql,它将返回查询的属性并允许您直接在RoR中编写SQL

https://apidock.com/rails/v6.1.3.1/ActiveRecord/Querying/find_by_sql

RegistrationAnswer.find_by_sql(
    <<~SQL.squish
      with tbl_numbered as (
          SELECT 
            registration_questions.original_id, registration_answers.id, registration_answers.updated_at
            ROW_NUMBER() OVER (PARTITION BY registration_questions.original_id ORDER BY registration_answers.updated_at DESC) as rk
          FROM 
            registration_answers
          JOIN registration_questions ON registration_question.id = registration_answers.registration_question_id
          WHERE registration_answers.profile = ? AND registration_question_id = ?
      )

      select id, updated_at from tmp_table where rk = 1
    SQL,
    [profile, related_question_ids]
)

这将返回 RegistrationAnswer 的实例,其 id 和 updated_at 仅 您可以检查它们返回的内容,然后映射 id 并重新查询表中的记录

您可以在 ruby​​ 中使用 select 按 ROW_NUM 进行过滤这可能会出现性能问题,具体取决于您的表有多大

recent_related_answers = RegistrationAnswer.joins(:registration_question).
                            where(profile: profile, registration_question_id: related_question_ids).
                            select("registration_questions.original_id", "updated_at", "id", "ROW_NUMBER() OVER (PARTITION BY registration_questions.original_id ORDER BY registration_answers.updated_at DESC) as RK")

                                                  
recent_related_answer_ids = recent_related_answers.select { |record| record.RK == 1 }.map(&:id)

查询可能需要一些更新,因为我不确定您的架构是什么样的 - 如果它不起作用,请告诉我

【讨论】:

  • 感谢您撰写本文。虽然我没有在我自己的应用程序中测试过这个,但这感觉就像我在寻找什么。我已授予赏金,感谢您的回答!
  • 谢谢伙计!如果它不起作用,请告诉我
猜你喜欢
  • 2011-04-12
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 2020-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多