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 |
+----------+--------+
我正在寻求使用存储过程根据另一个 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 |
+----------+--------+