Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment puis-je trouver la bonne taille de boîte pour chaque produit ?

La clé, bien sûr, est la jointure entre les deux tables. Je le montre d'abord séparément, plutôt que la requête complète, pour aider à la compréhension. Pour chaque article, nous retrouvons TOUTES les tailles de box pouvant accueillir l'article.

Dans tous les cas, la correspondance est possible si la hauteur du produit <=la hauteur de la boîte, et les deux autres dimensions correspondent, dans l'une ou l'autre permutation (les produits peuvent toujours être pivotés pour tenir dans la boîte, qu'ils soient layables ou non).

Uniquement pour les produits lavables, nous sommes autorisés à faire pivoter le produit dans les trois dimensions pour les insérer dans des boîtes. Cela signifie que, pour les produits lavables uniquement, nous pouvons comparer la largeur ou la profondeur du produit à la hauteur de la boîte, et comparer les deux dimensions restantes du produit à la largeur et à la profondeur de la boîte.

Une fois qu'on a compris ce que je viens de dire (comme on ferait ça sans ordinateur, juste avec un crayon sur papier), la traduction en code est quasi automatique :

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

Sortie :

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      

Pour chaque produit, nous avons trouvé TOUTES les tailles qui conviendraient.

Remarquez la jointure externe dans la requête, pour inclure les produits qui ne rentrent dans AUCUNE taille de boîte ; c'est le cas du produit j , qui apparaît à la fin de la sortie. Notez que j'utilise null comme marqueur pour "non disponible " - les mots "non disponible" n'ajoutent aucune information valable par rapport à la simple utilisation de null .

L'étape suivante est une simple agrégation - pour chaque produit, trouvez la plus petite taille qui fonctionne. Le meilleur outil pour cela est le FIRST fonction d'agrégation (telle qu'utilisée ci-dessous). Nous devons commander par taille de boîte; puisque les tailles sont S, M, L (qui sont dans l'ordre alphabétique inverse juste par accident), j'utilise le decode() pour attribuer 1 à S, 2 à M, 3 à L. La requête agrégée trouve la "première" taille qui fonctionne pour chaque produit.

L'important ici est que la requête peut être généralisée facilement à n'importe quel nombre de "tailles de boîte" possibles - même lorsque les trois dimensions ne sont pas dans un ordre croissant. (Vous pourriez aussi avoir des boîtes avec une seule des dimensions très grande alors que les autres sont petites, etc.). Vous pouvez commander par volume de boîtes, ou vous pouvez stocker dans le tableau des boîtes un ordre de préférence, équivalent à ce que je fais dans la requête avec le decode() fonction.

Au final, la requête et la sortie ressemblent à ceci. Notez que j'ai utilisé nvl() dans le select clause pour générer 'not available' pour le dernier élément, au cas où vous en auriez vraiment besoin (ce dont je doute, mais ce n'est pas mon problème professionnel.)

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