Ключът, разбира се, е съединението между двете таблици. Първо го показвам отделно, а не цялата заявка, за да ви помогне да разберете. За всеки артикул намираме ВСИЧКИ размери на кутията, които могат да поберат артикула.
Във всички случаи съвпадението е възможно, ако височината на продукта <=височината на кутията, а другите два измерения пасват, в която и да е пермутация (продуктите винаги могат да бъдат завъртани да се поберат в кутията, независимо дали могат да се полагат или не).
Само за продукти, които могат да се полагат, ни е позволено да завъртаме продукта във всичките три измерения, за да ги поставим в кутии. Това означава, че само за продукти, които могат да се полагат, можем да сравним ширината или дълбочината на продукта с височината на кутията и да сравним останалите два размера на продукта с ширината и дълбочината на кутията.
След като разберем какво казах току-що (както бихме направили това без компютри, само с молив върху хартия), преводът в код е почти автоматичен:
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)
)
;
Изход:
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
За всеки продукт намерихме ВСИЧКИ размери, които биха работили.
Обърнете внимание на външното съединение в заявката, за да включите продукти, които не се побират в НИКОЙ размер на кутията; такъв е случаят с продукт j
, който се появява в края на изхода. Имайте предвид, че използвам null
като маркер за „не е наличен " - думите "не е налично" не добавят ценна информация спрямо простото използване на null
.
Следващата стъпка е просто агрегиране - за всеки продукт намерете най-малкия размер, който работи. Най-добрият инструмент за това е FIRST
агрегатна функция (както се използва по-долу). Трябва да поръчаме по размер на кутията; тъй като размерите са S, M, L (които са в обратен азбучен ред съвсем случайно), използвам decode()
функция за присвояване на 1 на S, 2 на M, 3 на L. Обобщената заявка намира „първия“ размер, който работи за всеки продукт.
Важното тук е, че заявката може лесно да се обобщи до произволен брой възможни "размери на кутии" - дори когато не всичките три измерения са в нарастващ ред. (Можете също така да имате кутии само с един от размерите много голям, докато другите са малки и т.н.). Можете да поръчате по обем на кутията или да съхраните в таблицата с кутии ред на предпочитание, еквивалентен на това, което правя в заявката с decode()
функция.
В крайна сметка заявката и изходът изглеждат така. Имайте предвид, че използвах nvl()
в select
клауза за генериране на 'not available'
за последния артикул, в случай че наистина имате нужда от него (в което се съмнявам, но не е мой бизнес проблем.)
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