简体   繁体   English

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

[英]Can Rails' Active Record handle SQL aggregate queries?

Just started learning active record and am wondering how to best retrieve data from multiple tables where an SQL aggregate query is involved. 刚刚开始学习活动记录,我想知道如何从涉及SQL聚合查询的多个表中最佳地检索数据。

In the following example (from a medical app) I'm looking for the most recent events of various types for each patient (eg last visit, last labtest etc). 在以下示例中(来自医疗应用程序)我正在为每位患者寻找各种类型的最新事件(例如,上次访问,最后一次实验室测试等)。 As you can see from the sql query below I'm looking for the max(date) value from a grouped query. 正如您从下面的SQL查询中看到的,我正在寻找分组查询中的max(date)值。 I resorted to find_by_sql to do this - however I'd like to see how to do this without using find_by_sql. 我使用find_by_sql来执行此操作 - 但是我想看看如何在不使用find_by_sql的情况下执行此操作。

IOW - how would you get the required data here using a pure ActiveRecord approach. IOW - 如何使用纯ActiveRecord方法获取所需数据。 Below are the Table and Class defs I'm testing with: 下面是我正在测试的表和类定义:

Find by Sql to retrieve most recent entries for each type - note the 'max(event_date)' here 通过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"

Here's the sample sql query result: 这是示例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

Classes

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

As Pallan pointed out, the :select option cannot be used with the :include option. 正如Pallan指出的那样, :select选项不能与:include选项一起使用。 However the :joins option can. 但是:joins选项可以。 And this is what you want here. 这就是你想要的。 In fact, it can take the same arguments as :include or use your own SQL. 实际上,它可以采用相同的参数:include或使用您自己的SQL。 Here's some rough, untested code, may need some minor fiddling. 这是一些粗略的,未经测试的代码,可能需要一些小的摆弄。

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")

Note I modified things slightly. 注意我稍微修改了一下。 I renamed the event_date alias to max_date so there's no confusion over which attribute you are referring to. 我改名为event_date别名max_date所以没有混淆哪个属性你指的是。 The attributes used in your :select query are available in the models returned. 您的:select查询中使用的属性在返回的模型中可用。 For example, in this you can call event.max_date . 例如,您可以在此调用event.max_date I also added the event id column because you can sometimes get some nasty errors without an id attribute (depending on how you use the returned models). 我还添加了事件id列,因为有时你可以在没有id属性的情况下得到一些讨厌的错误(取决于你如何使用返回的模型)。

The primary difference between :include and :joins is that the former performs eager loading of the associated models. 以下:include:joins之间的主要区别在于前者执行关联模型的预先加载。 In other words, it will automatically fetch the associated patient object for each event. 换句话说,它将自动为每个事件获取相关的患者对象。 This requires control of the select statement because it needs to select the patient attributes at the same time. 这需要控制select语句,因为它需要同时选择患者属性。 With :joins the patient objects are not instantiated. :joins患者对象没有实例化。

include只是一个聪明的左连接,你可以在.find中与select和group_by结合使用

In the current version of AR (2.3) I think your only option is to use find_by_sql. 在当前版本的AR(2.3)中,我认为您唯一的选择是使用find_by_sql。 Why? 为什么? Your custom SELECT attributes. 您的自定义SELECT属性。 ActiveRecord allows :select and :include arguments to the find call. ActiveRecord允许:select和:包含查找调用的参数。 Unfortunately they can't be used together. 不幸的是它们不能一起使用。 If you specify both the :select will be ignored. 如果同时指定:select将被忽略。 In your example you need the select to limit your columns and to get your MAX function. 在您的示例中,您需要select来限制列并获得MAX函数。

I've heard there is a patch/hack to get them to work together, but I am not sure where it is. 我听说有一个补丁/黑客让他们一起工作,但我不知道它在哪里。

Peer 窥视

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM