填充SQL Server中日期之间的剩余日期

edqdpe6u  于 2023-01-08  发布在  SQL Server
关注(0)|答案(1)|浏览(137)

我在表中有以下数据:
| 项目ID|日期|现况|
| - ------|- ------|- ------|
| 零零一|2021年1月12日|活跃|
| 零零一|2021年1月16日|中止|
| 零零一|2021年1月20日|活跃|
我需要像这样填写剩余的日期:
| 项目ID|日期|现况|
| - ------|- ------|- ------|
| 零零一|2021年1月12日|活跃|
| 零零一|2021年1月13日|活跃|
| 零零一|2021年1月14日|活跃|
| 零零一|2021年1月15日|活跃|
| 零零一|2021年1月16日|中止|
| 零零一|2021年1月17日|中止|
| 零零一|2021年1月18日|中止|
| 零零一|2021年1月19日|中止|
| 零零一|2021年1月20日|活跃|
另外,我需要一些建议,告诉我这样填充数据是否有效,或者在数据仓库中为有效开始日期和有效结束日期创建两个不同的列是否有效?

gajydyqb

gajydyqb1#

我有一个可行的解决方案,但我相信有更好的方法来实现这一点。我假设您需要一个可行的解决方案,然后您可以调查性能并在需要时优化它。
正如在评论中指出的,要解决这个问题,如果你有一个日历表是最容易的。我假设你没有任何东西,所以我从头开始。我生成数字0 - 9,然后通过连续的CROSS JOINS我使用这些数字生成数字0 - 10,000。我确实假设不超过10个,000天,但如果这不正确,您可以更改代码以生成更多数字。
我的方法使用了几个常用的表表达式,因为这是我逐步解决问题的方式。因此,首先生成数字,然后生成数字,然后确定每个ItemID的最小和最大日期,然后创建一个记录集,其中包含每个ItemID的最小和最大日期之间的所有日期,然后使用LEFT JOIN this复制Status。最后,你会遇到一个有趣的问题,如何获取一列的最后一个非NULL值,有几种方法,下面是你可以看到的许多不同方法中的一个:https://www.mssqltips.com/sqlservertip/7379/last-non-null-value-set-of-sql-server-records/我使用了在窗口中使用MAX函数的方法。
因此,将所有这些放在一个脚本中,并从表变量中的数据开始(以及为另一个测试添加一些记录),整个过程如下所示:

DECLARE @Data TABLE([ItemID] VARCHAR(3), [Date] DATE, [Status] VARCHAR(15));

INSERT INTO @Data ([ItemID],[Date],[Status])
VALUES ('001', '2021-01-12', 'Active'), ('001', '2021-01-16','Discontinued'),('001', '2021-01-20','Active'), 
('002','2022-02-01','Active'), ('002','2022-03-01','Discontinued');

;WITH digits (I) AS 
(
    SELECT I
    FROM  (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS digits (I)
)
,integers (I) AS (
    SELECT D1.I + (10*D2.I) + (100*D3.I) + (1000*D4.I)      
    FROM digits AS D1 CROSS JOIN digits AS D2 CROSS JOIN digits AS D3 CROSS JOIN digits AS D4
), itemMinMaxDates AS (
    SELECT [ItemID], MIN([Date]) AS [MinDate], MAX([Date]) AS [MaxDate] 
    FROM @Data GROUP BY [ItemID]
), itemsWithAllDates AS 
(
    SELECT [imm].[ItemID], DATEADD(DAY,i.I, imm.[MinDate]) AS [Date] FROM [itemMinMaxDates] AS imm CROSS JOIN [integers] AS i 
    WHERE DATEADD(DAY,i.I, imm.[MinDate]) BETWEEN imm.[MinDate] AND imm.[MaxDate]
), itemsWithAllDatesAndStatus AS 
(
    SELECT [allDates].[ItemID], [allDates].[Date], [d].[Status] FROM [itemsWithAllDates] AS allDates 
    LEFT OUTER JOIN @Data AS d ON [allDates].[ItemID] = [d].[ItemID] AND [allDates].[Date] = d.[Date]
), grp AS 
(
    SELECT [itemsWithAllDatesAndStatus].[ItemID],
       [itemsWithAllDatesAndStatus].[Date],
       [itemsWithAllDatesAndStatus].[Status], 
       MAX(IIF([itemsWithAllDatesAndStatus].[Status] IS NOT NULL, [itemsWithAllDatesAndStatus].[Date], NULL)) OVER (PARTITION BY [itemsWithAllDatesAndStatus].[ItemID] ORDER BY [itemsWithAllDatesAndStatus].[Date] ROWS UNBOUNDED PRECEDING) AS grp
    FROM itemsWithAllDatesAndStatus 
) 
SELECT [grp].[ItemID], [grp].[Date], 
MAX([grp].[Status]) OVER (PARTITION BY [grp].[ItemID], grp  ORDER BY [grp].[Date] ROWS UNBOUNDED PRECEDING) AS [Status] 
FROM [grp] 
ORDER BY [grp].[ItemID], [grp].[Date];

结果就是你所展示的(以及我为测试所包含的数据):
| 项目ID|日期|现况|
| - ------|- ------|- ------|
| 零零一|2021年1月12日|活跃|
| 零零一|2021年1月13日|活跃|
| 零零一|2021年1月14日|活跃|
| 零零一|2021年1月15日|活跃|
| 零零一|2021年1月16日|中止|
| 零零一|2021年1月17日|中止|
| 零零一|2021年1月18日|中止|
| 零零一|2021年1月19日|中止|
| 零零一|2021年1月20日|活跃|
| 002| 2022年2月1日|活跃|
| 002| 2022年2月2日|活跃|
| 002| 2022年2月3日|活跃|
| 002| 2022年2月4日|活跃|
| 002| 2022年2月5日|活跃|
| 002| 2022年2月6日|活跃|
| 002| 2022年2月7日|活跃|
| 002| 2022年2月8日|活跃|
| 002| 2022年2月9日|活跃|
| 002| 2022年2月10日|活跃|
| 002| 2022年2月11日|活跃|
| 002| 2022年2月12日|活跃|
| 002| 2022年2月13日|活跃|
| 002| 2022年2月14日|活跃|
| 002| 2022年2月15日|活跃|
| 002| 2022年2月16日|活跃|
| 002| 2022年2月17日|活跃|
| 002| 2022年2月18日|活跃|
| 002| 2022年2月19日|活跃|
| 002| 2022年2月20日|活跃|
| 002|二○二二年二月二十一日|活跃|
| 002|二○二二年二月二十二日|活跃|
| 002|二〇二二年二月二十三日|活跃|
| 002|二○二二年二月二十四日|活跃|
| 002|二〇二二年二月二十五日|活跃|
| 002|二〇二二年二月二十六日|活跃|
| 002|二〇二二年二月二十七日|活跃|
| 002| 2022年2月28日|活跃|
| 002|二○二二年三月一日|中止|
正如我所说,这是一个可行的解决方案,但它可能不是最好或最有效的解决方案-但它可以让您启动和运行。

相关问题