Sch*_*ell 19 ruby-on-rails arel
我试图在Arel和/或Rails 3中的Active Record中嵌套SELECT查询以生成以下SQL语句.
SELECT sorted.* FROM (SELECT * FROM points ORDER BY points.timestamp DESC) AS sorted GROUP BY sorted.client_id
可以通过执行创建子查询的别名
points = Table(:points)
sorted = points.order('timestamp DESC').alias
但后来我被困在如何将它传递给父查询(缺少调用#to_sql,这听起来很难看).
如何使用SELECT语句作为Arel(或Active Record)中的子查询来完成上述操作?也许有一种完全不同的方式来完成这个不使用嵌套查询的查询?
tod*_*odd 24
这是我对临时表和Arel的处理方法.它使用Arel#from方法在内部查询中传递Arel#to_sql.
inner_query = YourModel.where(:stuff => "foo")
outer_query = YourModel.scoped  # cheating, need an ActiveRelation
outer_query = outer_query.from(Arel.sql("(#{inner_query.to_sql}) as results")).
                          select("*")
现在你可以使用outer_query,paginate,select,group等做一些不错的事情......
inner_query - >
select * from your_models where stuff='foo'
outer_query - >
select * from (select * from your_models where stuff='foo') as results;
问题是为什么你需要一个"嵌套查询"?我们不需要使用"嵌套查询",这是在SQL而不是关系代数的思维模式中思考的.使用关系代数,我们导出关系并使用一个关系的输出作为另一个关系的输入,因此以下将成立:
points = Table(:points, {:as => 'sorted'}) # rename in the options hash
final_points = points.order('timestamp DESC').group(:client_id, :timestamp).project(:client_id, :timestamp)
除非绝对必要,否则最好将重命名保留为arel.
这里client_id和时间戳的投影非常重要,因为我们无法从关系中投射所有域(即排序.*).您必须专门预测将在关系的分组操作中使用的所有域.原因是*没有任何值可以明确代表分组的client_id.比如说你有下表
client_id   |   score
----------------------
    4       |    27
    3       |    35
    2       |    22
    4       |    69
在这里,如果你分组,你不能在分数域上执行投影,因为该值可以是27或69,但你可以投射一个总和(分数)
您只能将具有唯一值的域属性投影到该组(通常是sum,max,min等聚合函数).根据您的查询,如果点按时间戳排序是无关紧要的,因为最终它们将按client_id分组.时间戳顺序无关紧要,因为没有可以表示分组的单个时间戳.
请告诉我如何帮助您解决Arel问题.此外,我一直致力于为人们使用Arel的学习系列.本系列的第一部分是http://Innovative-Studios.com/#pilot 我可以告诉你,你开始知道如何使用Table(:points)而不是ActiveRecord模型Point.
虽然我不认为这个问题需要嵌套查询,比如Snuggs提到的.对于那些确实需要嵌套查询的人.这是我到目前为止所做的工作,不是很好,但它有效:
class App < ActiveRecord::Base   
  has_many :downloads
  def self.not_owned_by_users(user_ids)
    where(arel_table[:id].not_in( 
      Arel::SqlLiteral.new( Download.from_users(user_ids).select(:app_id).to_sql ) ) )
  end
end
class Download  < ActiveRecord::Base
  belongs_to :app
  belongs_to :user
  def self.from_users(user_ids)
    where( arel_table[:user_id].in user_ids )
  end
end
class User < ActiveRecord::Base
  has_many :downloads
end
App.not_owned_by_users([1,2,3]).to_sql #=>
# SELECT `apps`.* FROM `apps` 
# WHERE (`apps`.`id` NOT IN (
#   SELECT app_id FROM `downloads` WHERE (`downloads`.`user_id` IN (1, 2, 3))))
#
Point.
 from(Point.order(Point.arel_table[:timestamp].desc).as("sorted")).
 select("sorted.*").
 group("sorted.client_id")
| 归档时间: | 
 | 
| 查看次数: | 14370 次 | 
| 最近记录: |