sql >> Database >  >> RDS >> Oracle

Hoe vind ik de juiste maat doos voor elk product?

De sleutel is natuurlijk de verbinding tussen de twee tabellen. Ik laat het eerst apart zien, in plaats van de volledige vraag, om het beter te begrijpen. Voor elk item vinden we ALLE doosformaten die geschikt zijn voor het item.

In alle gevallen is de match mogelijk als producthoogte <=dooshoogte, en de andere twee afmetingen passen, in beide permutaties (producten kunnen altijd worden gedraaid om in de doos te passen, of ze nu legbaar zijn of niet).

Alleen voor legbare producten mogen we het product in alle drie de dimensies roteren om ze in dozen te passen. Dit betekent dat we alleen voor legbare producten de breedte of diepte van het product kunnen vergelijken met de hoogte van de doos en de twee resterende afmetingen van het product kunnen vergelijken met de breedte en diepte van de doos.

Als we eenmaal begrijpen wat ik zojuist heb gezegd (zoals we dit zouden doen zonder computers, gewoon met potlood op papier), is de vertaling in code bijna 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)
          )
;

Uitgang:

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      

Voor elk product hebben we ALLE maten gevonden die zouden werken.

Let op de buitenste join in de query, om producten op te nemen die niet in ELKE doos passen; dat is het geval bij product j , die aan het einde van de uitvoer verschijnt. Merk op dat ik null . gebruik als een markering voor "niet beschikbaar " - de woorden "niet beschikbaar" voegen geen waardevolle informatie toe over het eenvoudige gebruik van null .

De volgende stap is een eenvoudige aggregatie - zoek voor elk product de kleinste maat die werkt. De beste tool hiervoor is de FIRST aggregatiefunctie (zoals hieronder gebruikt). We moeten bestellen op doosgrootte; aangezien de maten S, M, L zijn (die per ongeluk in omgekeerde alfabetische volgorde staan), gebruik ik de decode() functie om 1 aan S, 2 aan M, 3 aan L toe te wijzen. De geaggregeerde zoekopdracht vindt de "eerste" maat die voor elk product werkt.

Het belangrijkste hier is dat de zoekopdracht gemakkelijk kan worden gegeneraliseerd naar een willekeurig aantal mogelijke "doosgroottes" - zelfs als niet alle drie de dimensies in oplopende volgorde staan. (U kunt ook dozen hebben waarvan slechts één van de afmetingen erg groot is, terwijl de andere klein zijn, enz.). U kunt per doosvolume bestellen, of u kunt opslaan in de dozentabel een volgorde van voorkeur, gelijk aan wat ik doe in de query met de decode() functie.

Uiteindelijk zien de query en uitvoer er als volgt uit. Merk op dat ik nvl() . heb gebruikt in de select clausule om 'not available' te genereren voor het laatste item, voor het geval je het echt nodig hebt (wat ik betwijfel, maar het is niet mijn zakelijke probleem.)

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   


  1. PHP/MySQL - BEGIN... COMMIT werkt niet

  2. Bigquery:doorzoek meerdere tabellen en aggregeer met first_seen en last_seen

  3. MySQL-fout:mysql_fetch_assoc() verwacht dat parameter 1 resource is

  4. MySQL/PHP-updatequeryfout