La clave, por supuesto, es la unión entre las dos tablas. Lo muestro por separado primero, en lugar de la consulta completa, para ayudar a comprender. Para cada artículo, encontramos TODOS los tamaños de caja que pueden acomodar el artículo.
En todos los casos, la coincidencia es posible si la altura del producto <=altura de la caja, y las otras dos dimensiones encajan, en cualquier permutación (los productos siempre se pueden rotar que quepan en la caja, sean lavables o no).
Solo para productos lavables, se nos permite rotar el producto en las tres dimensiones para que encajen en las cajas. Esto significa que, solo para productos que se pueden colocar, podemos comparar el ancho o la profundidad del producto con la altura de la caja, y comparar las dos dimensiones restantes del producto con el ancho y la profundidad de la caja.
Una vez que entendemos lo que acabo de decir (como lo haríamos sin computadoras, solo con lápiz sobre papel), la traducción a código es casi 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)
)
;
Salida:
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 producto, encontramos TODOS los tamaños que funcionarían.
Observe la unión externa en la consulta, para incluir productos que no caben en NINGÚN tamaño de caja; ese es el caso del producto j
, que aparece al final de la salida. Tenga en cuenta que uso null
como marcador de "no disponible " - las palabras "no disponible" no agregan información valiosa sobre el simple uso de null
.
El siguiente paso es una agregación simple:para cada producto, encuentre el tamaño más pequeño que funcione. La mejor herramienta para esto es el FIRST
función agregada (como se usa a continuación). Debemos ordenar por tamaño de caja; dado que los tamaños son S, M, L (que están en orden alfabético inverso solo por accidente), uso el decode()
función para asignar 1 a S, 2 a M, 3 a L. La consulta agregada encuentra el "primer" tamaño que funciona para cada producto.
Lo importante aquí es que la consulta se puede generalizar fácilmente a cualquier número de "tamaños de caja" posibles, incluso cuando las tres dimensiones no están en orden creciente. (También podría tener cajas con solo una de las dimensiones muy grande mientras que las otras son pequeñas, etc.). Puede ordenar por volumen de caja, o puede almacenar en la tabla de cajas un orden de preferencia, equivalente a lo que hago en la consulta con decode()
función.
Al final, la consulta y la salida se ven así. Tenga en cuenta que usé nvl()
en el select
cláusula para generar 'not available'
para el último artículo, en caso de que realmente lo necesites (lo cual dudo, pero no es mi problema comercial).
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