“Rails方式”执行seml复杂查询

时间:2014-11-22 15:08:29

标签: ruby-on-rails postgresql rails-activerecord

我在使用postgres的Rails 4项目中有一个半复杂的查询。我想知道是否可以将查询转换为ActiveRecord / Arel,如果Rails惯例是使用ActiveRecord::Base.connection.execute(sql),则执行原始SQL是必要的。

这是我想要执行的查询:

select
  sum(case when is_graded = true then 1 else 0 end) graded,
  sum(case when is_canceled = true then 1 else 0 end) canceled,
  sum(case when is_graded IS NULL and is_canceled IS NULL then 1 else 0 end) in_progress
from exams
where user_id = 1 and quiz_id = 114;

我希望结果格式为:

{"graded"=>"2", "canceled"=>"2", "in_progress"=>"1"}

这让我得到了我正在寻找的答案,但它似乎很难看,我想看看是否有更好的方法:

sql="select
  sum(case when is_graded = true then 1 else 0 end) graded,
  sum(case when is_canceled = true then 1 else 0 end) canceled,
  sum(case when is_graded IS NULL and is_canceled IS NULL then 1 else 0 end) in_progress
from exams
where user_id = 1 and quiz_id = 114;"

result = ActiveRecord::Base.connection.execute(sql)

result.to_a.first

重申我的问题:

  1. 是否可以使用Arel / ActiveRecord编写此查询?如果是这样,怎么样?

  2. 它是" Rails惯例"如果需要在Rails中执行原始SQL,请使用ActiveRecord::Base.connection.execute(sql)This answer表示最好在SQL复杂时直接运行SQL,this answer是我找到ActiveRecord::Base.connection.execute(sql)的地方。

1 个答案:

答案 0 :(得分:1)

通过ActiveRecord的最简单方法可能就是这样:

Exam.where(:user_id => 1, :quiz_id => 114).select(%q{
  sum(case when is_graded then 1 else 0 end) graded,
  sum(case when is_canceled then 1 else 0 end) canceled,
  sum(case when is_graded IS NULL and is_canceled IS NULL then 1 else 0 end) in_progress
})[0]

这会在Exam中为您提供result个实例,但这不会是Exam id is_gradedgraded,...属性,此属性将具有canceledin_progressselect('...')属性。你看,当你说attributes时,AR会产生一些对象,这些对象的属性与SELECT子句中的命名列相匹配。

然后调用result = Exam.where(:user_id => 1, :quiz_id => 114).select(%q{...})[0].attributes # result is now like {"graded"=>"2", "canceled"=>"2", "in_progress"=>"1"} 方法会给你你的哈希:

{{1}}

你可能用AREL方法表达那些SUM和CASE表达式,但这将是一个非常难看的混乱,所以我不会打扰。