2

很抱歉重新上传了这个问题,但我真的很想得到答案。

请允许我再次提出这个问题,并希望您的支持。


问题是找到合适尺寸的盒子,让物流业务在运输时节省资金。

我们有 2 张桌子,分别是盒子和产品。

Boxes 表包含每个盒子的每个 ID 和尺寸。“w”代表宽,“d”代表深度,“h”代表高度。为方便起见,请假设我们只有 3 盒样品。

产品表还包括产品 ID、尺寸。尺寸与盒子表的含义相同。'layable' 是指产品不仅可以直接包装,还可以进行可放置。例如,产品“g”是一个易碎的瓶子,无法在盒子中放置水平位置。因此,这是可放置列中的“n”。

这个问题需要用正确的尺寸框查询每个产品ID。正确尺寸的盒子意味着产品需要与空间最小的盒子一起运输。

希望您的帮助。谢谢。

盒子:

BOX_SIZE W D H
小号 353 250 25
450 350 160
大号 610 460 460

产品:

ID W D H LAYABLE
一个 350 250 25 是的
b 450 250 160 是的
C 510 450 450 是的
d 350 250 25 是的
e 550 350 160 是的
F 410 400 430 n
G 350 240 25 n
H 450 350 160 n
一世 310 360 430 n
j 500 500 600 是的

预期输出:

ID BOX_SIZE
一个 小号
b
... ……
... ……
... ……
G 小号
H
一世 大号
j 无法使用

用于创建和填充表以进行测试的语句:

create table boxes
    ( box_size char(1) primary key
    , w        number  not null
    , d        number  not null
    , h        number  not null
    )
;

insert into boxes (box_size, w, d, h) values ('S', 353, 250,  25);
insert into boxes (box_size, w, d, h) values ('M', 450, 350, 160);
insert into boxes (box_size, w, d, h) values ('L', 610, 460, 460);

create table products
    ( id      varchar2(10) primary key
    , w       number       not null
    , d       number       not null
    , h       number       not null
    , layable char(1)      check(layable in ('y', 'n'))
    )
;

insert into products (id, w, d, h, layable) values ('a', 350, 250,  25, 'y');
insert into products (id, w, d, h, layable) values ('b', 450, 250, 160, 'y');
insert into products (id, w, d, h, layable) values ('c', 510, 450, 450, 'y');
insert into products (id, w, d, h, layable) values ('d', 350, 250,  25, 'y');
insert into products (id, w, d, h, layable) values ('e', 550, 350, 160, 'y');
insert into products (id, w, d, h, layable) values ('f', 410, 400, 430, 'n');
insert into products (id, w, d, h, layable) values ('g', 350, 240,  25, 'n');
insert into products (id, w, d, h, layable) values ('h', 450, 350, 160, 'n');
insert into products (id, w, d, h, layable) values ('i', 310, 360, 430, 'n');
insert into products (id, w, d, h, layable) values ('j', 500, 500, 600, 'y');    

commit;
4

2 回答 2

1

The key, of course, is the join between the two tables. I show it separately first, rather than the complete query, to help understanding. For each item, we find ALL the box sizes that can accommodate the item.

In all cases, the match is possible if product height <= box height, and the other two dimensions fit, in either permutation (products can always be rotated to fit in the box, whether they are layable or not).

Only for layable products, we are allowed to rotate the product in all three dimensions to fit them in boxes. This means that, for layable products only, we can compare product width or depth to box height, and compare the two remaining dimensions of the product to box width and depth.

Once we understand what I just said (as the way we would do this without computers, just with pencil on paper), the translation into code is almost automatic:

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

Output:

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      

For each product, we found ALL the sizes that would work.

Notice the outer join in the query, to include products that don't fit in ANY box size; that is the case of product j, which appears at the end of the output. Note that I use null as a marker for "not available" - the words "not available" add no valuable information over the simple use of null.

The next step is a simple aggregation - for each product, find the smallest size that works. The best tool for this is the FIRST aggregate function (as used below). We must order by box size; since the sizes are S, M, L (which are in reverse alphabetical order just by accident), I use the decode() function to assign 1 to S, 2 to M, 3 to L. The aggregate query finds the "first" size that works for each product.

The important thing here is that the query can be generalized easily to any number of possible "box sizes" - even when not all three dimensions are in increasing order. (You could also have boxes with only one of the dimensions very large while the others are small, etc.). You can order by box volume, or you can store in the boxes table an order of preference, equivalent to what I do in the query with the decode() function.

In the end, the query and output look like this. Note that I used nvl() in the select clause to generate 'not available' for the last item, in case you really need it (which I doubt, but it's not my business problem.)

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   
于 2021-02-13T16:24:17.293 回答
0

到目前为止,我可以提出自己的答案,如下所示,但这似乎每个产品 ID 都有三行。因此,这需要过滤到每个查询的最小框。我厌倦了使用 min(function) 但这会导致错误。

select p.id, p.h, p.w, p.d, p.layable, case
when p.layable = 'n' then case 
 when p.h <= b.h and 
((greatest(p.w, p.d ) <= greatest(b.w, b.d)) 
and (least(p.w, p.d) <= least(b.w, b.d))) then b.id
else 'no' end
when p.layable = 'y' then case 
when (p.h + p.w + p.d) <= (b.h + b.w + b.d) and 
((greatest(p.h, p.w, p.d) <= greatest(b.h, b.w, b.d))
and (least(p.h, p.w, p.d) <= least(b.h, b.w, b.d))) then b.id
else 'no' end
 else 'not available' end
from products p, boxes b
order by p.id;
于 2021-02-13T09:12:59.593 回答