Kluczem jest oczywiście połączenie dwóch stołów. Najpierw pokazuję je osobno, a nie całe zapytanie, aby pomóc w zrozumieniu. Dla każdego przedmiotu znajdujemy WSZYSTKIE rozmiary pudełek, które mogą pomieścić przedmiot.
We wszystkich przypadkach dopasowanie jest możliwe, jeśli wysokość produktu <=wysokość pudełka, a pozostałe dwa wymiary pasują do siebie w dowolnej kombinacji (produkty zawsze można obracać pasują do pudełka, niezależnie od tego, czy można je układać, czy nie).
Tylko w przypadku produktów do układania, możemy obracać produkt we wszystkich trzech wymiarach, aby zmieścić go w pudełkach. Oznacza to, że tylko w przypadku produktów do układania możemy porównać szerokość lub głębokość produktu z wysokością pudełka i porównać dwa pozostałe wymiary produktu z szerokością i głębokością pudełka.
Kiedy zrozumiemy to, co właśnie powiedziałem (tak jak zrobilibyśmy to bez komputerów, tylko ołówkiem na papierze), tłumaczenie na kod jest prawie automatyczne:
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)
)
;
Wyjście:
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
Dla każdego produktu znaleźliśmy WSZYSTKIE rozmiary, które by działały.
Zwróć uwagę na sprzężenie zewnętrzne w zapytaniu, aby uwzględnić produkty, które nie mieszczą się w DOWOLNYM rozmiarze pudełka; tak jest w przypadku produktu j
, który pojawia się na końcu danych wyjściowych. Zauważ, że używam null
jako znacznik „niedostępne " - słowa "niedostępne" nie dodają żadnych cennych informacji w porównaniu z prostym użyciem null
.
Następnym krokiem jest prosta agregacja - dla każdego produktu znajdź najmniejszy rozmiar, który działa. Najlepszym narzędziem do tego jest FIRST
funkcja agregująca (używana poniżej). Musimy zamówić według rozmiaru pudełka; ponieważ rozmiary to S, M, L (które są w odwrotnej kolejności alfabetycznej tylko przez przypadek), używam decode()
funkcja przypisania 1 do S, 2 do M, 3 do L. Zapytanie zbiorcze znajduje „pierwszy” rozmiar, który działa dla każdego produktu.
Ważną rzeczą tutaj jest to, że zapytanie można łatwo uogólnić na dowolną liczbę możliwych "rozmiarów pudełek" - nawet jeśli nie wszystkie trzy wymiary są ułożone w rosnącej kolejności. (Możesz również mieć pudełka, w których tylko jeden z wymiarów jest bardzo duży, podczas gdy inne są małe itp.). Możesz zamówić według objętości pudełka lub możesz przechowywać w tabeli pudeł kolejność preferencji, równoważna temu, co robię w zapytaniu za pomocą decode()
funkcja.
W końcu zapytanie i wyjście wyglądają tak. Zauważ, że użyłem nvl()
w select
klauzula generująca 'not available'
dla ostatniego elementu, na wypadek, gdybyś naprawdę go potrzebował (w co wątpię, ale to nie jest mój problem biznesowy).
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