T-SQL:根据另一个表组合行

时间:2017-10-27 10:24:36

标签: tsql sql-update sql-insert sql-server-2016

我正在寻求使用存储过程基于另一个表的信息来更改表内容。为了说明我的观点(并躲避我生锈的英语技能),我创建了以下简化。

我有一张表格的片段数量

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

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

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

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

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

简而言之,基于[dbo]。[possible_combinations]将组合片段添加到表中,并减少所需片段的数量。从表中删除耗尽的碎片。

如何以简单的方式实现此片段转换?我开始编写一个while循环,检查是否有足够的片段可用,在for循环内部,通过片段编号进行交互。但是,我无法提出功能数量检查,并开始怀疑这是否可能以这种方式在T-SQL中实现。

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

请务必注意,创建哪种组合并不重要。

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

更新

使用iamdave的解决方案,只要我不触摸它就可以正常工作,我收到以下错误消息:

列名或提供的值数与表定义不匹配。

我几乎没有改变任何东西。是否有可能使用超过必要列的现有表而不是声明表(如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]

1 个答案:

答案 0 :(得分:1)

您可以使用包含多个语句的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 |
+----------+--------+