1

首先,我是 Ruby/Rails 新手,所以如果这个问题是基本的,我深表歉意。

我有一个(除其他外)看起来像这样的数据库:

organizations { id, name, current_survey_id }
surveys { id, organization_id }
responses { id, survey_id, question_response_integer }

我正在尝试创建一个范围方法,将当前调查答案的平均值添加到传入的组织关系中。换句话说,传递给该方法的作用域将生成看起来或多或少像这样的 SQL:

select * from organizations

我希望范围在我的 lambda 处理后生成如下所示的 SQL:

select o.id, o.name, cs.average_responses
from organizations o join
(select r.id, avg(r.question_response_integer) as average_responses 
     from responses r 
     group by r.id) cs on cs.id = o.current_survey_id

我所拥有的最好的是这样的:

current_survey_average: lambda do |scope, sort_direction|
  average_answers = Responses.
     select("survey_id, avg(question_response_integer) as average_responses").
     group("survey_id")
  scope.joins(average_answers).order("average_responses #{sort_direction}")
end

这主要只是在黑暗中刺伤-除其他外,它没有指定范围可以如何加入average_answers-但我无法找到任何有关如何进行这种加入的文档,而且我我没有东西可以尝试了。

有什么建议么?

编辑:感谢 Sean Hill 的回答。只是为了记录下来,这是我最终使用的代码:

current_survey_average: lambda do |scope, sort_direction|
  scope_table = scope.arel.froms.first.name
  query = <<-QUERY
    inner join (
     select r.survey_id, avg(r.question_response_integer) as average_responses
      from responses r
      group by r.survey_id
    ) cs
    on cs.survey_id = #{scope_table}.current_survey_id
  QUERY
  scope.
    joins(query).
    order("cs.average_responses #{sort_direction}")
end

也就是说,我可以看到将averaged_answers作用域直接放在 Responses 类上的好处——所以我最终可能会这样做。

4

1 回答 1

1

我无法对此进行测试,但我认为以下内容会起作用,无论是按原样还是稍作调整。

class Response < ActiveRecord::Base
  scope :averaged, -> { select('r.id, avg(r.question_response_integer) as average_responses').group('r.id') }

  scope :current_survey_average, ->(incoming_scope, sort_direction) do
    scope_table = incoming_scope.arel.froms.first.name
    query = <<-QUERY
      INNER JOIN ( #{Arel.sql(averaged.to_sql)} ) cs
      ON cs.id = #{scope_table}.current_survey_id
    QUERY

    incoming_scope.joins(query).order("average_responses #{sort_direction}")
  end
end

所以我在这里所做的是将内部查询拆分为另一个称为平均的范围。由于您不知道传入范围current_survey_average来自哪个表,因此我通过scope.arel.froms.first.name. 然后我创建了一个使用范围的查询字符串并使用变量averaged加入它。scope_table其余的很不言自明。

如果您确实知道传入范围将始终来自组织表,那么您不需要额外的scope_table变量。您可以将其硬编码到连接查询字符串中。

我会提出一个建议。如果您无法控制sort_direction,那么我不会直接将其输入到订单字符串中。

于 2013-04-20T22:07:52.740 回答