Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como posso encontrar a caixa do tamanho certo para cada produto?


A chave, é claro, é a junção entre as duas tabelas. Mostro-o separadamente primeiro, em vez da consulta completa, para ajudar na compreensão. Para cada item, encontramos TODOS os tamanhos de caixa que podem acomodar o item.

Em todos os casos, a correspondência é possível se a altura do produto <=altura da caixa e as outras duas dimensões couberem, em qualquer permutação (os produtos sempre podem ser girados para caber na caixa, sejam eles laváveis ​​ou não).

Somente para produtos laváveis, podemos girar o produto em todas as três dimensões para encaixá-los em caixas. Isso significa que, apenas para produtos laváveis, podemos comparar a largura ou profundidade do produto com a altura da caixa e comparar as duas dimensões restantes do produto com a largura e a profundidade da caixa.

Uma vez que entendemos o que acabei de dizer (como faríamos isso sem computadores, apenas com lápis no papel), a tradução em código é quase automática:
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)
          )
;

Resultado:
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      

Para cada produto, encontramos TODOS os tamanhos que funcionariam.

Observe a junção externa na consulta, para incluir produtos que não cabem em QUALQUER tamanho de caixa; esse é o caso do produto j , que aparece no final da saída. Observe que eu uso null como um marcador para "não disponível " - as palavras "não disponível" não adicionam informações valiosas sobre o simples uso de null .

A próxima etapa é uma agregação simples - para cada produto, encontre o menor tamanho que funcione. A melhor ferramenta para isso é o FIRST função agregada (como usado abaixo). Devemos pedir pelo tamanho da caixa; como os tamanhos são S, M, L (que estão em ordem alfabética reversa apenas por acidente), eu uso o decode() função para atribuir 1 a S, 2 a M, 3 a L. A consulta agregada encontra o "primeiro" tamanho que funciona para cada produto.

O importante aqui é que a consulta pode ser generalizada facilmente para qualquer número de "tamanhos de caixa" possíveis - mesmo quando nem todas as três dimensões estão em ordem crescente. (Você também pode ter caixas com apenas uma das dimensões muito grandes, enquanto as outras são pequenas, etc.). Você pode pedir por volume de caixa ou pode armazenar na tabela de caixas uma ordem de preferência, equivalente ao que faço na consulta com o decode() função.

No final, a consulta e a saída ficam assim. Observe que usei nvl() no select cláusula para gerar 'not available' para o último item, caso você realmente precise (o que duvido, mas não é problema meu).
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