T-SQL:根据另一个 table 合并行

T-SQL: Combining rows based on another table

我正在寻求使用存储过程根据另一个 table 的信息更改一个 table 内容。为了表达我的观点(并避免我生疏的英语技能),我创建了以下简化。

我有一个 table 片段数量的形式

SELECT * FROM [dbo].[obtained_fragments] ->
fragment   amount
22         42
76         7
101        31
128        4
177        22
212        6

和一个 table,其中列出了将这些片段组合到其他片段的所有可能组合。

SELECT * FROM [dbo].[possible_combinations] ->
fragment   consists_of_f1   f1_amount_needed   consists_of_f2   f2_amount_needed
1001       128              1                  22               3
1004       151              1                  101              12
1012       128              1                  177              6
1047       212              1                  76               4

我的目标是改变第一个 table 以便执行所有可能的片段组合,从而导致

SELECT * FROM [dbo].[obtained_fragments] ->
fragment   amount
22         30
76         3
101        31
177        22
212        5
1001       4
1047       1

换言之,在table基础上[dbo].[possible_combinations]增加了组合分片,减少了需要分片的数量。从 table.

中删除耗尽的片段

如何轻松实现这种片段转换?我开始编写一个 while 循环,检查是否有足够的片段可用,在 for 循环内,遍历片段编号。但是,我无法提出功能性金额检查并开始怀疑这在 T-SQL 中是否可行。

代码不必非常高效,因为两个 table 总是小于 200 行。

需要注意的是,创建哪些组合并不重要。

[f1_amount_needed] 的值始终为 1 可能会派上用场。

更新

使用 iamdave 的解决方案,只要我不碰它就可以很好地工作,我收到以下错误消息:

列名称或提供的值数量与 table 定义不匹配。

我几乎没有真正改变任何东西。使用现有的 tables 而不是声明 tables(如 iamdave 所做的那样)是否有可能会产生这种差异?

DECLARE @t TABLE(Binding_ID int, Exists_of_Binding_ID_2 int, Exists_of_Pieces_2 int, Binding1 int, Binding2 int);

WHILE 1=1
BEGIN

    DELETE @t

    INSERT INTO @t
        SELECT TOP 1
             k.Binding_ID
            ,k.Exists_of_Binding_ID_2
            ,k.Exists_of_Pieces_2
            ,g1.mat_Binding_ID AS Binding1
            ,g2.mat_Binding_ID AS Binding2

        FROM [dbo].[vwCombiBinding] AS k

        JOIN [leer].[sandbox5] AS g1
        ON k.Exists_of_Binding_ID_1 = g1.mat_Binding_ID AND g1.Amount >= 1

        JOIN [leer].[sandbox5] AS g2
        ON k.Exists_of_Binding_ID_2 = g2.mat_Binding_ID AND g2.Amount >= k.Exists_of_Pieces_2

        ORDER BY k.Binding_ID

    IF (SELECT COUNT(1) FROM @t) = 1
        BEGIN

        UPDATE g
        SET Amount = g.Amount +1
        FROM [leer].[sandbox5] AS g

        JOIN @t AS t
        ON g.mat_Binding_ID = t.Binding_ID

        INSERT INTO [leer].[sandbox5]
        SELECT
             t.Binding_ID
            ,1
        FROM @t AS t
        WHERE NOT EXISTS (SELECT NULL FROM [leer].[sandbox5] AS g WHERE g.mat_Binding_ID = t.Binding_ID);

        UPDATE g
        SET Amount = g.Amount - 1
        FROM [leer].[sandbox5] AS g

        JOIN @t AS t
        ON g.mat_Binding_ID = t.Binding1

        UPDATE g
        SET Amount = g.Amount - t.Exists_of_Pieces_2
        FROM [leer].[sandbox5] AS g

        JOIN @t AS t
        ON g.mat_Binding_ID = t.Binding2

        END
    ELSE
        BREAK
END

SELECT * FROM [leer].[sandbox5]

您可以使用 while 循环执行此操作,该循环包含多个语句来处理迭代数据更新。由于您需要根据每次迭代对数据的重新评估进行更改,因此 必须 在某种循环中完成:

declare @f table(fragment int,amount int);
insert into @f values (22 ,42),(76 ,7 ),(101,31),(128,4 ),(177,22),(212,6 );

declare @c table(fragment int,consists_of_f1 int,f1_amount_needed int,consists_of_f2 int,f2_amount_needed int);
insert into @c values (1001,128,1,22,3),(1004,151,1,101,12),(1012,128,1,177,6),(1047,212,1,76,4);

declare @t table(fragment int,consists_of_f2 int,f2_amount_needed int,fragment1 int,fragment2 int);

while 1 = 1
begin
    -- Clear out staging area
    delete @t;

    -- Populate with the latest possible combination
    insert into @t
    select top 1 c.fragment
                ,c.consists_of_f2
                ,c.f2_amount_needed
                ,f1.fragment as fragment1
                ,f2.fragment as fragment2
    from @c as c
        join @f as f1
            on c.consists_of_f1 = f1.fragment
                and f1.amount >= 1
        join @f as f2
            on c.consists_of_f2 = f2.fragment
                and f2.amount >= c.f2_amount_needed
    order by c.fragment;

    -- Update fragments table if a new combination can be made
    if (select count(1) from @t) = 1
        begin

            -- Update if additional fragment
            update f
            set amount = f.amount + 1
            from @f as f
                join @t as t
                    on f.fragment = t.fragment;

            -- Insert if a new fragment
            insert into @f
            select t.fragment
                  ,1
            from @t as t
            where not exists(select null
                             from @f as f
                             where f.fragment = t.fragment
                            );

            -- Update fragment1 amounts
            update f
            set amount = f.amount - 1
            from @f as f
                join @t as t
                    on f.fragment = t.fragment1;

            -- Update fragment2 amounts
            update f
            set amount = f.amount - t.f2_amount_needed
            from @f as f
                join @t as t
                    on f.fragment = t.fragment2;

        end
    else    -- If no new combinations possible, break the loop
        break
end;

select *
from @f;

输出:

+----------+--------+
| fragment | amount |
+----------+--------+
|       22 |     30 |
|       76 |      3 |
|      101 |     31 |
|      128 |      0 |
|      177 |     22 |
|      212 |      5 |
|     1001 |      4 |
|     1047 |      1 |
+----------+--------+