Il record attivo di Rails può gestire query aggregate SQL?
-
10-07-2019 - |
Domanda
Ho appena iniziato a studiare i record attivi e mi chiedo come recuperare al meglio i dati da più tabelle in cui è coinvolta una query aggregata SQL.
Nel seguente esempio (da un'app medica) sto cercando gli eventi più recenti di vario tipo per ciascun paziente (ad es. ultima visita, ultimo test di laboratorio, ecc.). Come puoi vedere dalla query sql di seguito, sto cercando il valore massimo (data) da una query raggruppata. Ho fatto ricorso a find_by_sql per farlo - tuttavia mi piacerebbe vedere come farlo senza usare find_by_sql.
IOW: come otterresti i dati richiesti qui usando un approccio ActiveRecord puro. Di seguito sono riportati i parametri di tabella e classe con cui sto testando:
Trova per SQL per recuperare le voci più recenti per ogni tipo. Nota qui il "max (data_evento)"
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"
Ecco il risultato della query sql di esempio:
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
Corsi
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
Soluzione
Come ha sottolineato Pallan, l'opzione : select
non può essere utilizzata con l'opzione : include
. Tuttavia, l'opzione : joins
può. E questo è quello che vuoi qui. In effetti, può accettare gli stessi argomenti di : include
o utilizzare il proprio SQL. Ecco un po 'di codice approssimativo e non testato, potrebbe essere necessario un po' di confusione minore.
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")
Nota: ho modificato leggermente le cose. Ho rinominato l'alias event_date
in max_date
, quindi non c'è confusione sull'attributo a cui ti riferisci. Gli attributi utilizzati nella query : select
sono disponibili nei modelli restituiti. Ad esempio, in questo puoi chiamare event.max_date
. Ho anche aggiunto la colonna id
dell'evento perché a volte puoi ottenere alcuni errori spiacevoli senza un attributo id
(a seconda di come usi i modelli restituiti).
La differenza principale tra : include
e : join
è che il primo esegue un caricamento desideroso dei modelli associati. In altre parole, recupererà automaticamente l'oggetto paziente associato per ciascun evento. Ciò richiede il controllo dell'istruzione select
poiché deve selezionare contemporaneamente gli attributi del paziente. Con : join
gli oggetti paziente non vengono istanziati.
Altri suggerimenti
Un'inclusione è solo un'intelligente unione a sinistra, puoi usarla insieme a select e raggruppare nel tuo .find
Nell'attuale versione di AR (2.3) penso che la tua unica opzione sia quella di usare find_by_sql. Perché? I tuoi attributi SELECT personalizzati. ActiveRecord consente: selezionare e: include argomenti per la chiamata find. Sfortunatamente non possono essere usati insieme. Se si specifica sia: selezionare verrà ignorato. Nel tuo esempio è necessario selezionare per limitare le colonne e ottenere la funzione MAX.
Ho sentito che esiste una patch / hack per farli lavorare insieme, ma non sono sicuro di dove sia.
Peer