Может ли Rails Active Record обрабатывать агрегатные запросы SQL? - PullRequest
9 голосов
/ 02 августа 2009

Только что начал изучать активную запись, и мне интересно, как наилучшим образом извлечь данные из нескольких таблиц, где используется агрегированный запрос SQL.

В следующем примере (из медицинского приложения) я ищу самые последние события различных типов для каждого пациента (например, последний визит, последний лабораторный тест и т. Д.). Как вы можете видеть из запроса sql ниже, я ищу значение max (date) из сгруппированного запроса. Для этого я прибег к find_by_sql - однако я хотел бы увидеть, как это сделать без использования find_by_sql.

IOW - как бы вы получили здесь необходимые данные, используя чистый подход ActiveRecord. Ниже приведены определения таблиц и классов, с которыми я тестирую:

Поиск по 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 ]

14 голосов
/ 03 августа 2009

Как указал Паллан, параметр :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 объекты пациента не создаются.

2 голосов
/ 02 августа 2009

Включение - это просто умное соединение слева, вы можете использовать его вместе с select и group_by в вашем .find

0 голосов
/ 02 августа 2009

В текущей версии AR (2.3) я думаю, что ваш единственный вариант - использовать find_by_sql. Зачем? Ваши собственные атрибуты SELECT. ActiveRecord позволяет: select и: включать аргументы в вызов find. К сожалению, их нельзя использовать вместе. Если вы укажете оба: select будет игнорироваться. В вашем примере вам нужно выбрать, чтобы ограничить ваши столбцы и получить вашу функцию MAX.

Я слышал, что есть патч / хак, чтобы заставить их работать вместе, но я не уверен, где он.

Peer

...