首先,我是Ruby / Rails的新手,所以如果这个问题是基本的,我会道歉。
我有一个DB(其中包括)看起来像这样:
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类上的好处 - 所以我最终可能会这样做。
答案 0 :(得分: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
所以我在这里所做的就是我将内部查询拆分为另一个名为averaged的范围。由于您不知道current_survey_average
中的传入范围来自哪个表,因此我通过scope.arel.froms.first.name
获得了范围表名称。然后我创建了一个使用averaged
范围的查询字符串,并使用scope_table
变量将其连接起来。其余的都是不言自明的。
如果您确实知道传入范围将始终来自组织表,那么您不需要额外的scope_table
变量。您只需将其硬编码到连接查询字符串中即可。
我会提出一个建议。如果您无法控制sort_direction
,那么我不会直接将其输入到订单字符串中。