T-SQL - 填补运行平衡中的空白

发布于 2025-01-08 12:56:44 字数 977 浏览 3 评论 0原文

我正在开展一个数据仓库项目,客户提供每日销售数据。大多数产品线都提供现有数量,但经常缺失。我需要有关如何根据之前的 OH 和销售信息填充这些缺失值的帮助。

这是一个示例数据:

Line#  Store  Item  OnHand  SalesUnits  DateKey
-----------------------------------------------
1      001    A     100     20          1       
2      001    A     80      10          2       
3      001    A     null    30          3       --[OH updated with 70 (80-10)]
4      001    A     null    5           4       --[OH updated with 40 (70-30)]
5      001    A     150     10          5       --[OH untouched]
6      001    B     null    4           1       --[OH untouched - new item]
7      001    B     80      12          2       
8      001    B     null    10          3       --[OH updated with 68 (80-12]

第 1 行和第 2 行不会更新,因为存在现有数量。
第 3 行和第 4 行将根据其前面的行进行更新。
第 5 行保持不变,因为提供了 OnHand。
第 6 行保持不变,因为它是项目 B 的第一行

有没有办法在集合操作中做到这一点?我知道我可以使用 fast_forward 游标轻松完成此操作,但需要很长时间(15M+ 行)。

感谢您的帮助!

I am working on a Data Warehouse project and the client provides daily sales data. On-hand quantities are provided in most lines but are often missing. I need help on how to fill those missing values based on prior OH and sales information.

Here's a sample data:

Line#  Store  Item  OnHand  SalesUnits  DateKey
-----------------------------------------------
1      001    A     100     20          1       
2      001    A     80      10          2       
3      001    A     null    30          3       --[OH updated with 70 (80-10)]
4      001    A     null    5           4       --[OH updated with 40 (70-30)]
5      001    A     150     10          5       --[OH untouched]
6      001    B     null    4           1       --[OH untouched - new item]
7      001    B     80      12          2       
8      001    B     null    10          3       --[OH updated with 68 (80-12]

Lines 1 and 2 are not to be updated because OnHand quantities exist.
Lines 3 and 4 are to be updated based on their preceding rows.
Line 5 is to be left untouched because OnHand is provided.
Line 6 is to be left untouched because it is the first row for Item B

Is there a way I can do this in a set operation? I know I can do it easily using a fast_forward cursor but it will take a long time (15M+ rows).

Thanks for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

沦落红尘 2025-01-15 12:56:44

测试数据:

declare @t table(
Line# int,  Store char(3),  Item char,  OnHand int,  SalesUnits int, DateKey int
)

insert @t values
(1,  '001',  'A',  100,   20, 1),
(2,  '001',  'A',  80 ,   10, 2),
(3,  '001',  'A',  null,  30, 3),
(4,  '001',  'A',  null,   5, 4),
(5,  '001',  'A',  150,   10, 5),
(6,  '001',  'B',  null,   4, 1),
(7,  '001',  'B',  null,   4, 2),
(8,  '001',  'B',  80,    12, 3),
(9,  '001',  'B',  null,  10, 4)

不使用游标填充的脚本:

;with a as
(
select Line#,  Store,  Item,  OnHand,  SalesUnits, DateKey, 1 correctdata from @t where DateKey = 1
union all
select t.Line#,  t.Store,  t.Item,  coalesce(t.OnHand, a.onhand - a.salesunits),  t.SalesUnits, t.DateKey, t.OnHand from @t t
join a on a.DateKey = t.datekey - 1 and a.item = t.item and a.store = t.store
)
update t
set OnHand = a.onhand 
from @t t join a on a.line# = t.line#
where a.correctdata is null

使用游标填充的脚本:

declare @datekey int, @store int, @item char, @Onhand int, 
@calculatedonhand int, @salesunits int, @laststore int, @lastitem char

DECLARE sales_cursor 
CURSOR FOR  
SELECT datekey+1, store, item, OnHand -SalesUnits, salesunits
FROM @t sales  
order by store, item, datekey

OPEN sales_cursor;  
FETCH NEXT FROM sales_cursor  
INTO @datekey, @store, @item, @Onhand, @salesunits

WHILE @@FETCH_STATUS = 0 
BEGIN  
SELECT @calculatedonhand = case when @laststore = @store and @lastitem = @item 
then coalesce(@onhand, @calculatedonhand - @salesunits) else null end
,@laststore = @store, @lastitem = @item

UPDATE s
SET onhand=@calculatedonhand
FROM @t s
WHERE datekey = @datekey and @store = store and @item = item
and onhand is null and @calculatedonhand is not null

FETCH NEXT FROM sales_cursor  
INTO @datekey, @store, @item, @Onhand, @salesunits

END 
CLOSE sales_cursor; 
DEALLOCATE sales_cursor; 

我建议您使用游标版本,我怀疑使用递归查询是否可以获得不错的性能。我知道这里的人讨厌游标,但当你的表有这样的大小时,它可能是唯一的解决方案。

Test data:

declare @t table(
Line# int,  Store char(3),  Item char,  OnHand int,  SalesUnits int, DateKey int
)

insert @t values
(1,  '001',  'A',  100,   20, 1),
(2,  '001',  'A',  80 ,   10, 2),
(3,  '001',  'A',  null,  30, 3),
(4,  '001',  'A',  null,   5, 4),
(5,  '001',  'A',  150,   10, 5),
(6,  '001',  'B',  null,   4, 1),
(7,  '001',  'B',  null,   4, 2),
(8,  '001',  'B',  80,    12, 3),
(9,  '001',  'B',  null,  10, 4)

Script to populate not using cursor:

;with a as
(
select Line#,  Store,  Item,  OnHand,  SalesUnits, DateKey, 1 correctdata from @t where DateKey = 1
union all
select t.Line#,  t.Store,  t.Item,  coalesce(t.OnHand, a.onhand - a.salesunits),  t.SalesUnits, t.DateKey, t.OnHand from @t t
join a on a.DateKey = t.datekey - 1 and a.item = t.item and a.store = t.store
)
update t
set OnHand = a.onhand 
from @t t join a on a.line# = t.line#
where a.correctdata is null

Script to populate using cursor:

declare @datekey int, @store int, @item char, @Onhand int, 
@calculatedonhand int, @salesunits int, @laststore int, @lastitem char

DECLARE sales_cursor 
CURSOR FOR  
SELECT datekey+1, store, item, OnHand -SalesUnits, salesunits
FROM @t sales  
order by store, item, datekey

OPEN sales_cursor;  
FETCH NEXT FROM sales_cursor  
INTO @datekey, @store, @item, @Onhand, @salesunits

WHILE @@FETCH_STATUS = 0 
BEGIN  
SELECT @calculatedonhand = case when @laststore = @store and @lastitem = @item 
then coalesce(@onhand, @calculatedonhand - @salesunits) else null end
,@laststore = @store, @lastitem = @item

UPDATE s
SET onhand=@calculatedonhand
FROM @t s
WHERE datekey = @datekey and @store = store and @item = item
and onhand is null and @calculatedonhand is not null

FETCH NEXT FROM sales_cursor  
INTO @datekey, @store, @item, @Onhand, @salesunits

END 
CLOSE sales_cursor; 
DEALLOCATE sales_cursor; 

I recommand you use the cursor version, I doubt you can get a decent performance using the recursive query. I know people in here hate cursors, but when your table has that size, it can be the only solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文