Ich benutze Rails 5.0.0.1 ATM und ich habe Probleme mit ActiveRecord Beziehungen bei der Optimierung der Anzahl meiner DB Anfragen. Im Moment habe ich: Modell A (sagen wir 'Bestellungen'), Modell B ('OrderDispatches'), Modell C ('Person') und Modell D ('PersonVersion').ActiveRecord benutzerdefinierte has_one Beziehungen
Tabelle 'Menschen' besteht nur aus 'ID' und 'versteckte' Flagge, Rest der Leute Daten sitzt in 'Person_Versionen' ('Name', 'Nachname' und einige Dinge, die sich im Laufe der Zeit ändern können, wie wissenschaftlicher Titel).
Jede Bestellung hat 'receiving_person_id' wie für die Person, die den Auftrag in der DB erfasst hat und jeder OrderDispatch hat 'dispatching_person_id' für die Person, die den Auftrag geliefert hat. Auch Order und OrderDispatch haben Erstellungszeit.
Eine Bestellung hat viele Versendungen.
Die einfachen Beziehungen so ist:
has_many :receiving_person, through: :person, foreign_key: "receiving_person_id", class_name: 'PersonVersion'
Aber wenn ich meine Bestellung mit nach Depeschen Liste muß ich mit N + 1 Situation umgehen, denn genau zu finden (nach dem Erstellungsdatum der Bestellung/OrderDispatch) PersonVersion für jede receiving_person_id und dispatching_person_id Ich mache noch eine Anfrage.
SELECT *
FROM person_versions
WHERE effective_date_from <= ? AND person_id = ?
ORDER BY effective_date_from
LIMIT 1
Erstes "?" ist das Erstellungsdatum von Order/OrderDispatch und das zweite '?' empfängt die ID der Person.
Mit dieser Abfrage bekomme ich genaue Personendaten für die Zeit der Bestellung/OrderDispatch Erstellung.
Es ist ziemlich einfach zu schreiben Abfrage mit Unterabfrage (oder Unterabfragen, wie Order mit OrderDispatches auf einer Liste) in Raw SQL, aber ich habe keine Ahnung, wie dies mit ActiveRecord zu tun.
Ich habe versucht, individuelle has_one Beziehung zu schreiben, da dies so weit wie ich bin gekommen ist:
has_one :receiving_person. -> {
where("person_versions.id = (
SELECT id
FROM person_versions sub_pv1
WHERE sub_pv1.date_from <= orders.receive_date
AND sub_pv1.person_id = orders.receiving_person_id
LIMIT 1)")},
through: :person, class_name: "PersonVersion", primary_key: "person_id", source: :person_version
Es funktioniert, wenn ich diese nur verwenden, für die Aufnahme oder die Person Dispatching. Wenn ich versuche, dies für verknüpfte Orders und order_dispatches-Tabellen zu laden, dann muss einer von 'person_versions' Aliasing sein und in meiner benutzerdefinierten where-Klausel ist das nicht möglich (es gibt keine Möglichkeit vorherzusagen, ob es Aliasing wird oder nicht) .
Verschiedener aproach wäre dies:
has_one :receiving_person, -> {
where(:id => PersonVersion.where("
person_versions.date_from <= orders.receive_date
AND person_versions.person_id = orders.receiving_person_id").order(date_from: :desc).limit(1)},
through: :person, class_name: "PersonVersion", primary_key: "person_id", source: :person_version
Raw 'person_versions' in dem in Ordnung ist, weil es in Unterabfrage ist und mit Symbol ': id' macht rohe SQL-Tabelle Aufträge verbunden richtige Aliase für person_versions bekommen und order_dispatches, aber ich bekomme 'IN' anstelle von 'eqauls' für person_versions.id xx subquery und MySQL kann LIMIT nicht in Unterabfragen ausführen, die mit IN/ANY/ALL-Anweisungen verwendet werden, daher bekomme ich zufällige person_version.
Also TL; DR Ich muss 'has_many through' nach 'has_one' transformieren mit benutzerdefinierten 'where' Klausel, die nach neuesten Datensatz unter denen sucht, deren Datum niedriger ist als die ursprüngliche Datensatzerstellung.
EDIT: Eine andere TL; DR zur Vereinfachung
def receiving_person
receiving_person_id = self.receiving_person_id
receive_date = self.receive_date
PersonVersion.where(:person_id => receiving_person_id, :hidden => 0).where.has{date_from <= receive_date}.order(date_from: :desc, id: :desc).first
end
Ich brauche diese Methode umgewandelt 'has_one' Beziehung, so dass ich konnte 'eager_load' diese.
Eigentlich zu lindern, sollte es wie mehr aussehen, wenn ich werde diese, so tun: 'Klasse Order
Xenor
Diese Änderung ist eine Überlegung wert, obwohl gerade jetzt, wenn jemand vergisst, mir zu sagen, dass ihr Name oder eine andere Spezifikationen haben sich geändert Ich kann die Version mit effective_start_date in der Vergangenheit erstellen und sie wird alle früheren Bestellungen und Versendungen korrekt beeinflussen, da die Versionssuche dynamisch durchgeführt wird. Mit Ihrem Ansatz sollte ich einige Logik machen, um alle Bestellungen und Order_dispatches zu überprüfen, wenn ich neue Person_Version mit Effective_Start_Date in der Vergangenheit erstellen oder Effective_Start_Date ändern. Es ist ziemlich selten, aber ich muss immer noch mit meinen Kollegen über ein verändertes Schema gehen. Danke für die Antwort! – Xenor