Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie finde ich die richtige Kartongröße für jedes Produkt?

Der Schlüssel ist natürlich der Join zwischen den beiden Tabellen. Ich zeige es zuerst separat und nicht die vollständige Abfrage, um das Verständnis zu erleichtern. Für jeden Artikel finden wir ALLE Kartongrößen, die für den Artikel geeignet sind.

In allen Fällen ist die Übereinstimmung möglich, wenn Produkthöhe <=Kartonhöhe und die anderen beiden Abmessungen passen, in jeder Permutation (Produkte können immer gedreht werden in die Box passen, egal ob sie verlegbar sind oder nicht).

Nur bei legefähigen Produkten dürfen wir das Produkt in allen drei Dimensionen drehen, um es in Kartons zu passen. Das bedeutet, dass wir nur für verlegbare Produkte die Produktbreite oder -tiefe mit der Kartonhöhe und die beiden verbleibenden Abmessungen des Produkts mit der Kartonbreite und -tiefe vergleichen können.

Sobald wir verstanden haben, was ich gerade gesagt habe (wie wir es ohne Computer tun würden, nur mit Bleistift auf Papier), erfolgt die Übersetzung in Code fast automatisch:

select p.id, b.box_size
from   products p left outer join boxes b
       on
            p.h <= b.h and least   (p.w, p.d) <= least   (b.w, b.d)
                       and greatest(p.w, p.d) <= greatest(b.w, b.d)
       or
       p.layable = 'y'
          and
          ( p.w <= b.h and least   (p.h, p.d) <= least   (b.w, b.d)
                       and greatest(p.h, p.d) <= greatest(b.w, b.d)
            or
            p.d <= b.h and least   (p.w, p.h) <= least   (b.w, b.d)
                       and greatest(p.w, p.h) <= greatest(b.w, b.d)
          )
;

Ausgabe:

ID  BOX_SIZE
--- --------
a   S       
a   M       
a   L       
b   M       
b   L       
c   L       
d   S       
d   M       
d   L       
e   L       
f   L       
g   S       
g   M       
g   L       
h   M       
h   L       
i   L       
j      

Für jedes Produkt haben wir ALLE Größen gefunden, die funktionieren würden.

Beachten Sie den äußeren Join in der Abfrage, um Produkte einzuschließen, die in KEINE Kistengröße passen; das ist bei Produkt j der Fall , das am Ende der Ausgabe erscheint. Beachten Sie, dass ich null verwende als Markierung für „nicht verfügbar " - Die Worte "nicht verfügbar" fügen keine wertvollen Informationen über die einfache Verwendung von null hinzu .

Der nächste Schritt ist eine einfache Aggregation – finden Sie für jedes Produkt die kleinste Größe, die funktioniert. Das beste Werkzeug dafür ist FIRST Aggregatfunktion (wie unten verwendet). Wir müssen nach Kartongröße bestellen; Da die Größen S, M, L sind (die zufällig in umgekehrter alphabetischer Reihenfolge sind), verwende ich den decode() Funktion zum Zuweisen von 1 zu S, 2 zu M, 3 zu L. Die aggregierte Abfrage findet die „erste“ Größe, die für jedes Produkt funktioniert.

Wichtig dabei ist, dass sich die Abfrage leicht auf beliebig viele mögliche „Kästchengrößen“ verallgemeinern lässt – auch wenn nicht alle drei Dimensionen in aufsteigender Reihenfolge vorliegen. (Sie könnten auch Schachteln haben, bei denen nur eine der Abmessungen sehr groß ist, während die anderen klein sind usw.). Sie können nach Kartonvolumen bestellen oder in der Kartontabelle speichern eine Präferenzreihenfolge, äquivalent zu dem, was ich in der Abfrage mit decode() mache Funktion.

Am Ende sehen Abfrage und Ausgabe so aus. Beachten Sie, dass ich nvl() verwendet habe im select -Klausel zum Generieren von 'not available' für das letzte Element, falls Sie es wirklich brauchen (was ich bezweifle, aber es ist nicht mein geschäftliches Problem.)

select p.id, 
       nvl(  min(b.box_size) keep (dense_rank first 
             order by decode(b.box_size, 'S', 1, 'M', 2, 'L', 3))
          , 'not available') as box_size
from   products p left outer join boxes b
       on
            p.h <= b.h and least   (p.w, p.d) <= least   (b.w, b.d)
                       and greatest(p.w, p.d) <= greatest(b.w, b.d)
       or
       p.layable = 'y'
          and
          ( p.w <= b.h and least   (p.h, p.d) <= least   (b.w, b.d)
                       and greatest(p.h, p.d) <= greatest(b.w, b.d)
            or
            p.d <= b.h and least   (p.w, p.h) <= least   (b.w, b.d)
                       and greatest(p.w, p.h) <= greatest(b.w, b.d)
          )
group  by p.id
;

ID  BOX_SIZE
--- --------
a   S       
b   M       
c   L       
d   S       
e   L       
f   L       
g   S       
h   M       
i   L       
j   not available