[英]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.