Rails的Active Record可以处理SQL聚合查询吗?

时间:2009-08-01 21:14:47

标签: sql ruby-on-rails activerecord aggregate

刚刚开始学习活动记录,我想知道如何从涉及SQL聚合查询的多个表中最佳地检索数据。

在以下示例中(来自医疗应用程序)我正在为每位患者寻找各种类型的最新事件(例如,上次访问,最后一次实验室测试等)。正如您从下面的SQL查询中看到的,我正在寻找分组查询中的max(date)值。我使用find_by_sql来执行此操作 - 但是我想看看如何在不使用find_by_sql的情况下执行此操作。

IOW - 如何使用纯ActiveRecord方法获取所需数据。下面是我正在测试的表和类defs:

通过Sql查找以检索每种类型的最新条目 - 请注意这里的'max(event_date)'

strsql = "select  p.lname, e.patient_id, e.event_type, max(e.event_date) as event_date 
     from events e   
         inner join patients p on e.patient_id = p.id
         group by p.lname, e.patient_id, e.event_type"

以下是示例sql查询结果:

lname, patient_id, event_type, latest
'Hunt', 3, 'Labtest', '2003-05-01 00:00:00'
'Hunt', 3, 'Visit', '2003-03-01 00:00:00'
'Seifer', 2, 'Labtest', '2002-05-01 00:00:00'
'Seifer', 2, 'Visit', '2002-03-01 00:00:00'

Table Relationships are:
Tables ---> Patients --> Events
                               --> visits
                               --> labtests
                               --> ... other
patients
      t.string :lname
      t.date :dob

events
      t.column :patient_id, :integer
      t.column :event_date, :datetime
      t.column :event_type, :string

visits 
      t.column :event_id, :integer
      t.column :visittype, :string

labtests
      t.column :event_id, :integer
      t.column :testtype, :string
      t.column :testvalue, :string

class Patient < ActiveRecord::Base
  has_many :events
  has_many :visits, :through =>:events
  has_many :labtests, :through => :events
end

class Event < ActiveRecord::Base
  has_many :visits
  has_many :labtests
  belongs_to :patient
end

class Visit < ActiveRecord::Base
  belongs_to :event
end

class Labtest < ActiveRecord::Base
    belongs_to :event
end

3 个答案:

答案 0 :(得分:14)

正如Pallan所指出的,:select选项不能与:include选项一起使用。但是:joins选项可以。这就是你想要的。实际上,它可以采用与:include相同的参数或使用您自己的SQL。这是一些粗略的,未经测试的代码,可能需要一些小的摆弄。

Event.all(:select => "events.id, patients.lname, events.patient_id, events.event_type, max(events.event_date) as max_date", :joins => :patient, :group => "patients.lname, events.patient_id, events.event_type")

注意我稍微修改了一些东西。我将event_date别名重命名为max_date,因此您不会混淆您所指的属性。您返回的模型中提供了:select查询中使用的属性。例如,您可以在此处拨打event.max_date。我还添加了事件id列,因为如果没有id属性,您有时可能会遇到一些令人讨厌的错误(取决于您使用返回的模型的方式)。

:include:joins之间的主要区别在于前者执行关联模型的急切加载。换句话说,它将自动为每个事件获取相关的患者对象。这需要控制select语句,因为它需要同时选择患者属性。使用:joins时,患者对象不会被实例化。

答案 1 :(得分:2)

包含只是一个聪明的左连接,您可以在.find

中将其与select和group by结合使用

答案 2 :(得分:0)

在当前版本的AR(2.3)中,我认为您唯一的选择是使用find_by_sql。为什么?您的自定义SELECT属性。 ActiveRecord允许:select和:包含查找调用的参数。不幸的是它们不能一起使用。如果同时指定:select将被忽略。在您的示例中,您需要select来限制列并获得MAX函数。

我听说有一个补丁/黑客让他们一起工作,但我不知道它在哪里。

对等