Ich benutze Rails 5. Ich habe das folgende Modell ...
class Order < ApplicationRecord
...
has_many :line_items, :dependent => :destroy
Das LineItem-Modell hat das Attribut "discount_applied". Ich möchte alle Bestellungen zurückgeben, bei denen es keine Instanzen einer Werbebuchung gibt, bei denen das Feld "discount_applied" nicht null ist. Wie schreibe ich eine solche Finder-Methode?
Antworten:
Nicht effizient, aber ich dachte, es könnte Ihr Problem lösen:
Update :
Anstatt Bestellungen zu finden, für die alle Werbebuchungen keinen Rabatt haben, können wir alle Bestellungen mit Werbebuchungen mit einem Rabatt vom Ausgabeergebnis ausschließen. Dies kann mit einer Unterabfrage innerhalb der where-Klausel erfolgen:
Sie können sie in einer einzigen Finder-Methode kombinieren:
Hoffe das hilft
quelle
Zunächst hängt dies wirklich davon ab, ob Sie einen reinen Arel-Ansatz verwenden möchten oder ob die Verwendung von SQL in Ordnung ist. Ersteres ist IMO nur ratsam, wenn Sie beabsichtigen, eine Bibliothek zu erstellen, aber nicht erforderlich, wenn Sie eine App erstellen, bei der es in Wirklichkeit sehr unwahrscheinlich ist, dass Sie Ihr DBMS auf dem Weg ändern (und wenn Sie dies tun, eine Handvoll davon ändern Manuelle Abfragen sind wahrscheinlich das geringste Problem.
Vorausgesetzt, die Verwendung von SQL ist in Ordnung, ist die einfachste Lösung, die für nahezu alle Datenbanken funktionieren sollte, folgende:
Dies sollte auch so ziemlich überall funktionieren (und hat ein bisschen mehr Arel und weniger manuelles SQL):
Abhängig von Ihrem spezifischen DBMS (genauer gesagt dem entsprechenden Abfrageoptimierer) ist das eine oder andere möglicherweise leistungsfähiger.
Ich hoffe, das hilft.
quelle
Ein möglicher Code
Ich empfehle, sich mit der AR-Dokumentation für Abfragemethoden vertraut zu machen.
Aktualisieren
Dies scheint jedoch mehr interessiert zu sein als ich anfangs. Und komplizierter, so dass ich Ihnen keinen Arbeitscode geben kann. Aber ich würde eine Lösung mit untersuchen
LineItem.group(order_id).having(discount_applied: nil)
, die Ihnen eine Sammlung von line_items geben und sie dann als Unterabfrage verwenden sollte, um verwandte Bestellungen zu finden.quelle
Wenn Sie alle Datensätze möchten, bei denen discount_applied gleich Null ist, dann:
Order.includes(:line_items).where.not(line_items: {discount_applied: nil})
(Verwendung beinhaltet, um n + 1 Problem zu vermeiden) oder
quelle
Hier ist die Lösung für Ihr Problem
Erste Abfrage wird wieder Ide
Orders
mit mindestens einerline_item
mitdiscount_applied
. Die zweite Abfrage gibt alle zurück, beiorders
denen es keine Instanzen von aline_item
mit dem gibtdiscount_applied
.quelle
Ich würde die
NOT EXISTS
Funktion von SQL verwenden, die zumindest in MySQL und PostgreSQL verfügbar istes sollte so aussehen
quelle
Wenn ich das richtig verstanden habe, möchten Sie alle Bestellungen erhalten, für die für keine Position (falls vorhanden) ein Rabatt gewährt wird.
Eine Möglichkeit, diese Bestellungen zu erhalten,
ActiveRecord
wäre die folgende:Hier ist eine kurze Erklärung, wie das funktioniert:
left_outer_joins
, vorausgesetzt, Sie greifen nicht bei jeder Bestellung auf die Werbebuchungen zu. Sie können auchleft_joins
einen Alias verwenden.Order
Instanz instanziieren müssen, fügen Sie.eager_load(:line_items)
sie der Kette hinzu, um zu verhindern, dass für jede Bestellung (N + 1) eine zusätzliche Abfrage ausgeführt wird, dhorder.line_items.each
in einer Ansicht.distinct
ist wichtig, um sicherzustellen, dass Bestellungen nur einmal im Ergebnis enthalten sind.Aktualisieren
Meine vorherige Lösung bestand darin, dies nur
discount_applied IS NULL
für mindestens eine Werbebuchung zu überprüfen , nicht für alle. Die folgende Abfrage sollte die von Ihnen benötigten Bestellungen zurückgeben.Das ist was los ist:
orders LEFT OUTER JOIN line_items
) verwenden, damit Bestellungen ohne zugehörige Artikel enthalten sind.Order
Objekt zu erhalten, unabhängig davon, wie viele Elemente es enthält (GROUP BY recipes.id
).HAVING (COUNT(line_items.discount_applied) = 0)
).Ich hoffe das hilft.
quelle
Sie können dies mit einem klassischen Rails left_joins nicht effizient tun, aber SQL Left Join wurde erstellt, um diese Fälle zu behandeln
Ein einfacher innerer Join gibt alle Bestellungen zurück, die mit allen line_items verknüpft sind.
Wenn jedoch keine line_items für diese Bestellung vorhanden sind, wird die Reihenfolge ignoriert (wie ein falsches where)
. Wenn beim linken Join keine line_items gefunden wurden, verbindet sql sie mit einem leerer Eintrag, um ihn zu behalten
Also sind wir zu den line_items gegangen, die wir nicht wollen, und haben alle Bestellungen gefunden, die mit einem leeren line_items verbunden sind
Und vermeiden Sie jeglichen Code mit
where(id: pluck(:id))
oderhaving("COUNT(*) = 0")
, an diesem Tag wird Ihre Datenbank zerstörtquelle