Nyckeln är naturligtvis sammanfogningen mellan de två borden. Jag visar det separat först, snarare än hela frågan, för att underlätta förståelsen. För varje vara hittar vi ALLA lådstorlekar som kan rymma föremålet.
I alla fall är matchningen möjlig om produkthöjd <=lådhöjd och de andra två dimensionerna passar, i endera permutationen (produkter kan alltid roteras för att få plats i lådan, oavsett om de går att lägga eller inte).
Endast för utläggningsbara produkter får vi rotera produkten i alla tre dimensionerna för att passa dem i lådor. Detta innebär att vi, endast för utläggningsbara produkter, kan jämföra produktens bredd eller djup med lådans höjd, och jämföra produktens två återstående dimensioner med lådans bredd och djup.
När vi väl förstår vad jag just sa (som vi skulle göra detta utan datorer, bara med penna på papper), är översättningen till kod nästan automatisk:
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)
)
;
Utdata:
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 varje produkt hittade vi ALLA storlekar som skulle fungera.
Lägg märke till den yttre sammanfogningen i frågan, för att inkludera produkter som inte passar i NÅGON boxstorlek; det är fallet med produkten j
, som visas i slutet av utgången. Observera att jag använder null
som en markör för "inte tillgänglig " - orden "ej tillgängligt" tillför ingen värdefull information över den enkla användningen av null
.
Nästa steg är en enkel sammanställning - för varje produkt, hitta den minsta storleken som fungerar. Det bästa verktyget för detta är FIRST
aggregatfunktion (som används nedan). Vi måste beställa efter boxstorlek; eftersom storlekarna är S, M, L (som är i omvänd alfabetisk ordning av misstag) använder jag decode()
funktion för att tilldela 1 till S, 2 till M, 3 till L. Den aggregerade frågan hittar den "första" storleken som fungerar för varje produkt.
Det viktiga här är att frågan enkelt kan generaliseras till valfritt antal möjliga "boxstorlekar" - även när inte alla tre dimensionerna är i ökande ordning. (Man kan också ha lådor med bara en av dimensionerna väldigt stor medan de andra är små osv). Du kan beställa efter boxvolym eller så kan du förvara i boxtabellen en preferensordning, motsvarande vad jag gör i frågan med decode()
funktion.
I slutändan ser frågan och utdata ut så här. Observera att jag använde nvl()
i select
sats för att generera 'not available'
för det sista föremålet, om du verkligen behöver det (vilket jag tvivlar på, men det är inte mitt affärsproblem.)
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