Oracle
 sql >> Database >  >> RDS >> Oracle

Come posso trovare la scatola della misura giusta per ogni prodotto?

La chiave, ovviamente, è l'unione tra i due tavoli. Lo mostro prima separatamente, piuttosto che la query completa, per aiutare a capire. Per ogni articolo troviamo TUTTE le dimensioni della scatola che possono ospitare l'articolo.

In tutti i casi, la corrispondenza è possibile se l'altezza del prodotto <=altezza della scatola e le altre due dimensioni si adattano, in una delle due permutazioni (i prodotti possono sempre essere ruotati da inserire nella scatola, che siano posabili o meno).

Solo per i prodotti posabili, possiamo ruotare il prodotto in tutte e tre le dimensioni per inserirlo nelle scatole. Ciò significa che, solo per i prodotti posabili, possiamo confrontare la larghezza o la profondità del prodotto con l'altezza della scatola e confrontare le due dimensioni rimanenti del prodotto con la larghezza e la profondità della scatola.

Una volta compreso ciò che ho appena detto (come lo faremmo senza computer, solo con matita su carta), la traduzione in codice è quasi automatica:

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

Uscita:

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      

Per ogni prodotto abbiamo trovato TUTTE le taglie adatte.

Notare il join esterno nella query, per includere prodotti che non rientrano in QUALSIASI dimensione della scatola; questo è il caso del prodotto j , che appare alla fine dell'output. Nota che uso null come indicatore di "non disponibile " - le parole "non disponibili" non aggiungono informazioni preziose sul semplice utilizzo di null .

Il passaggio successivo è una semplice aggregazione:per ogni prodotto, trova la dimensione più piccola che funziona. Lo strumento migliore per questo è il FIRST funzione di aggregazione (come usata di seguito). Dobbiamo ordinare per dimensione della scatola; poiché le taglie sono S, M, L (che sono in ordine alfabetico inverso per caso), uso il decode() funzione per assegnare 1 a S, 2 a M, 3 a L. La query aggregata trova la "prima" dimensione che funziona per ogni prodotto.

La cosa importante qui è che la query può essere generalizzata facilmente a qualsiasi numero di possibili "dimensioni della scatola", anche quando non tutte e tre le dimensioni sono in ordine crescente. (Potresti anche avere scatole con una sola delle dimensioni molto grande mentre le altre sono piccole, ecc.). Puoi ordinare in base al volume delle scatole oppure puoi conservare nella tabella delle scatole un ordine di preferenza, equivalente a quello che faccio nella query con decode() funzione.

Alla fine, la query e l'output sono simili a questo. Nota che ho usato nvl() nel select clausola per generare 'not available' per l'ultimo articolo, nel caso ne avessi davvero bisogno (cosa di cui dubito, ma non è un mio problema di affari.)

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