2009-08-01 14 views
9

Właśnie zacząłem uczyć się aktywnego zapisu i zastanawiam się, jak najlepiej pobrać dane z wielu tabel, w które zaangażowane jest zapytanie agregujące SQL.Czy rekord aktywny Active Rails obsługuje zapytania agregacyjne SQL?

W poniższym przykładzie (z aplikacji medycznej) szukam najnowszych zdarzeń różnych typów dla każdego pacjenta (np. Ostatniej wizyty, ostatniego testu laboratoryjnego itp.). Jak widać z poniższego zapytania sql szukam wartości max (date) z kwerendy zgrupowanej. W tym celu uciekłem się do find_by_sql - jednak chciałbym zobaczyć, jak to zrobić bez użycia find_by_sql.

IOW - w jaki sposób uzyskałbyś wymagane dane tutaj, używając czystego podejścia ActiveRecord. Poniżej znajduje się tabela i defs Klasa I 'm testowania z:

znaleźć, SQL, aby pobrać najnowsze dane dla każdego typu - zwrócić uwagę na „max (EVENT_DATE)” tutaj

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" 

Oto przykładowe zapytanie sql wynik:

 
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 

Klasy

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 

Odpowiedz

14

Jak Pallan wskazał, opcja :select nie może być używany z :include opcja. Jednak opcja :joins może. I tego właśnie chcesz tutaj. W rzeczywistości może przyjąć te same argumenty, co :include lub użyć własnego kodu SQL. Oto jakiś szorstki, niesprawdzony kod, może potrzebować jakiegoś pomniejszego manipulowania.

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

Uwaga Zmodyfikowałem trochę. Zmieniłem nazwę na alias event_date na max_date, więc nie ma wątpliwości co do tego, do którego atrybutu się odwołujesz. Atrybuty użyte w zapytaniu :select są dostępne w modelach zwróconych. Na przykład w tym można zadzwonić pod numer event.max_date. Dodałem również kolumnę o zdarzeniu id, ponieważ czasami można uzyskać nieprzyjemne błędy bez atrybutu id (w zależności od tego, w jaki sposób korzystasz ze zwróconych modeli).

Podstawową różnicą między :include i :joins jest to, że pierwsza wykonuje przyspieszone ładowanie skojarzonych modeli. Innymi słowy, automatycznie pobierze powiązany obiekt pacjenta dla każdego zdarzenia. Wymaga to kontroli instrukcji select, ponieważ musi wybrać atrybuty pacjenta w tym samym czasie. W przypadku obiektów :joins obiekty pacjentów nie są tworzone instancjami.

+0

Szybkie śledzenie tutaj z OP.Z 1 drobnym usprawnieniem: join (vs: join) kod Ryana działa i zastępuje zapytanie Find_By_Sql w moim oryginalnym wpisie. Bardzo fajny. Jednak na marginesie zauważę tylko, że (AFAIK) nie wszystkie kwerendy SQL mogą być implementowane za pośrednictwem wywołań ORM (na przykład unijne zapytanie, które łączy wartości Min i Max z tabeli). Podejrzewam, że będzie to wymagało 2 oddzielnych wywołań za pośrednictwem ORM, podczas gdy Find_By_Sql może to zrobić w jednym wywołaniu np. find_by_sql ("Wybierz Min (t.Event_Date) z tabeli t Union Select Max (t.Event_Date) z tabeli t") – BrendanC

+0

Zaktualizowano oryginalny wpis do użycia: join. Masz jednak rację, nie każde zapytanie można wykonać za pomocą ORM, a to nie jest celem Active Record. Został celowo zaprojektowany, aby pasował do większości potrzeb, a resztę pozostawić do dyspozycji za pomocą find_by_sql. Nie starają się zrobić wszystkiego, więc nie wahaj się użyć find_by_sql, jeśli lepiej pasuje do pracy. – ryanb

0

W obecnej wersji AR (2.3) Myślę yo jedyną opcją jest użycie find_by_sql. Czemu? Twoje niestandardowe atrybuty SELECT. ActiveRecord pozwala: select i: include argumenty do wywołania find. Niestety nie można ich używać razem. Jeśli podasz oba: select zostanie zignorowane. W twoim przykładzie musisz wybrać, aby ograniczyć kolumny i uzyskać funkcję MAX.

Słyszałem, że istnieje łatka/hack, aby zachęcić ich do współpracy, ale nie jestem pewien, gdzie to jest.

Peer

+0

Czy istnieje sposób na użycie wyraźnego: dołącz zamiast i włącz, aby obejść to ograniczenie? Czy to byłaby opcja tutaj? – BrendanC

2

include jest tylko sprytny lewej przyłączyć, można użyć, że w połączeniu z select i group_by w .find

Powiązane problemy