Может ли активная запись Rails обрабатывать SQL-запросы SQL?

Просто начал изучать активную запись и задаюсь вопросом, как наилучшим образом извлекать данные из нескольких таблиц, в которых участвует 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

Ответ 1

Как отметил Паллан, опция :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

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

Ответ 3

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

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

Peer