关于组合来自两个表的数据的复杂查询

问题描述 投票:2回答:1

最近我有以下要求。有两张桌子。

-- lots of items
declare @items table(id varchar(10), pieces integer)
-- boxes
declare @boxes table(num varchar(10), capacity integer)



insert @items(id, pieces)
select 'l1', 5
union all select 'l2', 12
union all select 'l3', 8

insert @boxes(num, capacity)
select 'o1',2
union all select 'o2', 8
union all select 'o3', 2
union all select 'o4', 5
union all select 'o5', 9
union all select 'o6', 5


-- list all pairs of items-boxes. So that item will be put in what order
-- example: o1-l1, o2-l1, o2-l2, o3-l2, ...

请让我解释一下当务之急:有两张桌子。一个带有另一个带盒子的物品。我们需要通过以下方式将所有项目放入框中:

我们取第一个项目l1和第一个方框o1。项目l1有5个,盒子o1容量为2.我们只能在盒子o1中放入最多2个。所以我们创建第一行:

o1-l1

我们已经填满了o1。移动到下一个框o2。它的容量为8,我们有项目l1,剩下3件。将l1的左边部分放入框o2中,因此我们创建了第二条记录:

o2-l1

我们已将项目l1的所有部分放入框中。转到下一个项目l2。它有12件。我们在盒子o2中剩下5个容量。所以我们将5个l2放入o2并创建下一个记录:

o2-l2

然后我们按顺序选择下一个框并创建以下记录:

o3-l2

这样我们就可以生成行,直到我们将所有项目“放”到框中。生成的查询应该是这样的:

o1-l1
o2-l1
o2-l2
o3-l2
...

它可以通过CURSOR和东西在T-SQL中以必要的方式解决,这在性能上并不好。是否有任何SQL查询可以生成所需的输出?

sql-server tsql sql-server-2012 window-functions
1个回答
1
投票

Ilya Sh,我无法确定你自己的问题答案是否正确。但这是我的方法。

最初我认为这可以通过递归查询来解决,但是这就是所有框大小相等并且保证大于项目的位置,并且项目是不可分割的但是大小不同(因此唯一的决定与每个item是否可以打包在当前框中,或者必须进入下一个框)。

在这种情况下,我们有项目组(即指定项目类型和数量的行),其中项目大小相等但组可分割,因此每个项目组可以分布在多个框中,并且每个框可以包含多个项目组的部分,在框和项目组之间存在多对多关系。

我的想法是,每个盒子根据其容量,具有许多可以接收单个物品的单个“槽”(即一定体积的空间)。

我接近解决方案的方法是使用“数字表”将每个盒子/项目组的数量扩展到单独的盒子插槽和单个项目 - 每个盒子一行一行,每个项目一行。在我的机器上,我有一个名为zx_numbers的表 - 但是我在下面包含了代码,为了便于说明,它消除了对该表的依赖。

一旦我们以这种方式对数据进行标准化 - 通过将框扩展到各自的插槽中,并将项目组和汇总数量扩展到单个项目中 - 整个批次中的每个框插槽和项目按顺序编号,然后两者简单地连接在一起在那个序列号上。

我使用了FULL OUTER JOIN来保存无与伦比的插槽/物品。这为我们提供了一个非常通用且适应性强的问题解决方案,然后我们可以通过各种方式进一步处理以获得我们想要的特定数据(在这种情况下,只是盒子项组组合的摘要)。

我当前编写查询的方式是,未填充空格的框(或在所有框完全填充后留下余数的项目组)将保留在结果中,并放在最后,但如果不需要,可以过滤这些框。

WITH 
item_groups(item_group_id, group_qty) AS 
(
    select 'l1', 5
    union all select 'l2', 12
    union all select 'l3', 8
    --union all select 'l4', 8
)

,boxes(box_id, capacity) AS 
(
    select 'o1',2
    union all select 'o2', 8
    union all select 'o3', 2
    union all select 'o4', 5
    union all select 'o5', 9
    union all select 'o6', 5
)

,zx_numbers(zx_number) AS
(
    --SELECT * FROM dbo.zx_numbers

    --I have a dedicated numbers table on my machine, but I've substituted a 
    --manual sequence generator for the purposes of a self-contained demonstration
    SELECT
        (ones.n) + (10 * tens.n) + (100 * hundreds.n) AS zx_number

    FROM --range 0 to 999
         (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS ones(n)
        ,(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS tens(n)
        ,(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS hundreds(n)
)

,items AS
(
    SELECT 
        item_groups.*
        ,zx_number AS group_item_number
        ,ROW_NUMBER() OVER (ORDER BY item_group_id, zx_number) AS batch_item_number

    FROM
        item_groups

    INNER JOIN
        zx_numbers
        ON (zx_number BETWEEN 1 AND item_groups.group_qty)
)

,box_slots AS
(
    SELECT 
        boxes.*
        ,zx_number AS box_slot_number
        ,ROW_NUMBER() OVER (ORDER BY box_id, zx_number) AS batch_slot_number

    FROM
        boxes

    INNER JOIN
        zx_numbers
        ON (zx_number BETWEEN 1 AND boxes.capacity)
)

,box_item_matches AS
(
    SELECT
        COALESCE(bxsl.batch_slot_number, itms.batch_item_number) AS slot_number

        ,bxsl.box_id
        ,bxsl.capacity
        ,bxsl.box_slot_number

        ,itms.item_group_id
        ,itms.group_qty
        ,itms.group_item_number

    FROM
        box_slots AS bxsl

    FULL OUTER JOIN
        items AS itms
        ON (bxsl.batch_slot_number = itms.batch_item_number)
)

--SELECT * FROM box_item_matches

SELECT 
    box_id
    ,item_group_id

FROM 
    box_item_matches 

GROUP BY
    box_id, item_group_id

ORDER BY 
    IIF(box_id IS NULL OR item_group_id IS NULL, 1, 0) --i.e. NULLS LAST
    ,box_id
    ,item_group_id
© www.soinside.com 2019 - 2024. All rights reserved.