Question

Je viens tout juste d'apprendre l'apprentissage actif et je me demande comment récupérer au mieux les données de plusieurs tables dans lesquelles une requête d'agrégat SQL est impliquée.

Dans l'exemple suivant (tiré d'une application médicale), je recherche les événements les plus récents de différents types pour chaque patient (par exemple, la dernière visite, le dernier test de laboratoire, etc.). Comme vous pouvez le voir à partir de la requête SQL ci-dessous, je recherche la valeur max (date) d'une requête groupée. J'ai eu recours à find_by_sql pour le faire. Toutefois, j'aimerais savoir comment faire cela sans utiliser find_by_sql.

IOW - comment obtiendriez-vous les données requises ici en utilisant une approche purement ActiveRecord? Vous trouverez ci-dessous les définitions de table et de classe que je teste avec:

Rechercher par sql pour récupérer les entrées les plus récentes pour chaque type - notez le 'max (date_événement)' ici

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"

Voici l'exemple de résultat de la requête 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
Était-ce utile?

La solution

Comme l'a souligné Pallan, l'option : select ne peut pas être utilisée avec l'option : include . Cependant, l'option : join peut. Et c'est ce que vous voulez ici. En fait, il peut prendre les mêmes arguments que : include ou utiliser votre propre code SQL. Voici un code approximatif, non testé, qui peut nécessiter quelques manipulations mineures.

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

Notez que j'ai légèrement modifié les choses. J'ai renommé l'alias event_date en max_date afin d'éviter toute confusion quant à l'attribut auquel vous faites référence. Les attributs utilisés dans votre requête : select sont disponibles dans les modèles renvoyés. Par exemple, vous pouvez appeler date_événement.max_date . J'ai également ajouté la colonne id de l'événement, car vous pouvez parfois obtenir quelques erreurs désagréables sans attribut id (selon l'utilisation des modèles renvoyés).

La principale différence entre : include et : joint est que l'ancien effectue un chargement rapide des modèles associés. En d'autres termes, il va automatiquement chercher l'objet patient associé pour chaque événement. Cela nécessite le contrôle de l'instruction select car il doit sélectionner les attributs du patient en même temps. Avec : join , les objets patient ne sont pas instanciés.

Autres conseils

Une inclusion est juste une jointure gauche intelligente, vous pouvez l'utiliser conjointement avec select et group by dans votre .find

Dans la version actuelle d’AR (2.3), je pense que votre seule option est d’utiliser find_by_sql. Pourquoi? Vos attributs SELECT personnalisés. ActiveRecord permet: de sélectionner et d'inclure des arguments à l'appel de recherche. Malheureusement, ils ne peuvent pas être utilisés ensemble. Si vous spécifiez les deux: la sélection sera ignorée. Dans votre exemple, vous devez sélectionner pour limiter vos colonnes et obtenir votre fonction MAX.

J'ai entendu dire qu'il existe un correctif / hack pour les amener à travailler ensemble, mais je ne sais pas trop où il se trouve.

Peer

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top