填充缺失的日期,给定日期范围。

huangapple go评论66阅读模式
英文:

Fill missing dates given ranges of dates

问题

我有一个包含开始和结束日期的表格,如下所示:

开始时间 结束时间
2023-05-19 20:00:00.000 2023-05-22 05:30:00.000
2023-05-22 05:30:00.000 2023-05-24 11:30:00.000

我想要一个查询,将它们拆分成如下形式:

开始时间 结束时间
2023-05-19 20:00:00.000 2023-05-19 23:59:59.000
2023-05-20 00:00:00.000 2023-05-20 23:59:59.000
2023-05-21 00:00:00.000 2023-05-21 23:59:59.000
2023-05-22 00:00:00.000 2023-05-22 05:30:00.000

我尝试过 https://stackoverflow.com/a/57142843/22022398 但在跨越1天时不起作用。

开始时间 结束时间
2023-05-19 20:00:00.000 2023-05-19 23:59:59.000
2023-05-22 00:00:00.000 2023-05-22 05:30:00.000

我的SQL服务器是MSSQL15,所以我不能使用generate_series()。

我还尝试过 https://stackoverflow.com/a/25911919/22022398 ,但无法弄清如何将其应用于整个表格,而不是单个日期。

英文:

I have a table full of start and end dates like

Start End
2023-05-19 20:00:00.000 2023-05-22 05:30:00.000
2023-05-22 05:30:00.000 2023-05-24 11:30:00.000

I would like one select query that returns all of them split into

Start End
2023-05-19 20:00:00.000 2023-05-19 23:59:59.000
2023-05-20 00:00:00.000 2023-05-20 23:59:59.000
2023-05-21 00:00:00.000 2023-05-21 23:59:59.000
2023-05-22 00:00:00.000 2023-05-22 05:30:00.000

I've tried https://stackoverflow.com/a/57142843/22022398
But it does not work when it's more than 1 day

Start End
2023-05-19 20:00:00.000 2023-05-19 23:59:59.000
2023-05-22 00:00:00.000 2023-05-22 05:30:00.000

My SQL server is MSSQL15 so I can't use generate_series()

I've also tried https://stackoverflow.com/a/25911919/22022398 but can't figure out how to implement it for a whole table instead of a single date

答案1

得分: 2

以下是已翻译的代码部分:

使用递归查询,生成所有需要的日期,然后检查哪些日期保留原始日期值,哪些日期需要更新为时间 '00:00:00'  '23:59:59',使用窗口函数在 case 表达式内部。

WITH cte AS (
    SELECT ROW_NUMBER() OVER(ORDER BY Start_) AS rn,
           Start_                        AS firstdate, 
           Start_                        AS nextdate,
           End_                          AS lastdate
    FROM tab

    UNION ALL 

    SELECT rn, firstdate, DATEADD(dd, 1, nextdate) AS nextdate, lastdate
    FROM cte
    WHERE DATEADD(dd, 1, nextdate) < lastdate
)
SELECT rn, 
       CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate) > 1
            THEN CAST(nextdate AS DATE)
            ELSE nextdate 
       END AS [Start],
       CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate DESC) > 1
            THEN DATEADD(ss, -1, CAST(DATEADD(dd, 1, CAST(lastdate AS DATE)) AS DATETIME))
            ELSE lastdate
       END AS [End]
FROM cte
ORDER BY rn, [Start]

输出

rn 开始日期 结束日期
1 2023-05-19 20:00:00.000 2023-05-22 23:59:59.000
1 2023-05-20 00:00:00.000 2023-05-22 23:59:59.000
1 2023-05-21 00:00:00.000 2023-05-22 05:30:00.000
2 2023-05-22 05:30:00.000 2023-05-24 23:59:59.000
2 2023-05-23 00:00:00.000 2023-05-24 23:59:59.000
2 2023-05-24 00:00:00.000 2023-05-24 11:30:00.000

此处检查演示。

英文:

You can use a recursive query, that generates all and only your needed dates and then checks which are the ones to retain the original date values, and which are the ones to be updated with time '00:00:00' or '23:59:59', using window functions within case expressions.

WITH cte AS (
    SELECT ROW_NUMBER() OVER(ORDER BY Start_) AS rn,
           Start_                        AS firstdate, 
           Start_                        AS nextdate,
           End_                          AS lastdate
    FROM tab

    UNION ALL 

    SELECT rn, firstdate, DATEADD(dd, 1, nextdate) AS nextdate, lastdate
    FROM cte
    WHERE DATEADD(dd, 1, nextdate) &lt; lastdate
)
SELECT rn, 
       CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate) &gt; 1
            THEN CAST(nextdate AS DATE)
            ELSE nextdate 
       END AS [Start],
       CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate DESC) &gt; 1
            THEN DATEADD(ss, -1, CAST(DATEADD(dd, 1, CAST(lastdate AS DATE)) AS DATETIME))
            ELSE lastdate
       END AS [End]
FROM cte
ORDER BY rn, [Start]

Output:

rn Start End
1 2023-05-19 20:00:00.000 2023-05-22 23:59:59.000
1 2023-05-20 00:00:00.000 2023-05-22 23:59:59.000
1 2023-05-21 00:00:00.000 2023-05-22 05:30:00.000
2 2023-05-22 05:30:00.000 2023-05-24 23:59:59.000
2 2023-05-23 00:00:00.000 2023-05-24 23:59:59.000
2 2023-05-24 00:00:00.000 2023-05-24 11:30:00.000

Check the demo here.

答案2

得分: 1

以下是翻译好的内容:

类似这样将起作用。我建议使用实际的数字表,而不是公用表达式(CTE),但这是你可以自行解决的事情。

    创建临时表 #tmp(StartDT datetime,	EndDT datetime)
    插入到 #tmp	
    ('2023-05-19 20:00:00.000',	'2023-05-22 05:30:00.000'),
    ('2023-05-22 05:30:00.000',	'2023-05-24 11:30:00.000')
    
    ;使用 NUMS 作为
    (
    选择顶部 365
      行号() 排在第一位( c1.[object_id] 排序) RN
     sys.all_columns c1
      交叉连接 sys.all_columns  c2
    )
    选择  
    	情况 WHEN RN = 1 THEN StartDT  
    		其他时候 CAST(DATEADD(day, RN-1,CAST(StartDT as date) ) as datetime	)
    	结束 NewStart
    	,
    	情况 WHEN RN = DATEDIFF(day, t1.StartDT,t1.EndDT)+1 
    		其他时候
    		DATEADD(second,-1,	CAST(DATEADD(day, RN,CAST(StartDT as date)) as datetime))
    	结束 EndDT
    
     #tmp t1
    交叉适用(选择 RN from NUMS where RN &lt;= DATEDIFF(day, t1.StartDT,t1.EndDT)+1) DayNums
     NEWStart 排序
英文:

Something like this will do the trick. I would suggest using an actual numbers table instead of a CTE, but that is something you can tackle on your own.

CREATE TABLE #tmp(StartDT datetime,	EndDT datetime)
INSERT INTO #tmp	VALUES
(&#39;2023-05-19 20:00:00.000&#39;,	&#39;2023-05-22 05:30:00.000&#39;),
(&#39;2023-05-22 05:30:00.000&#39;,	&#39;2023-05-24 11:30:00.000&#39;)


;WITH NUMS AS
(
SELECT TOP 365
  ROW_NUMBER() OVER(ORDER BY c1.[object_id]) RN
FROM sys.all_columns c1
  CROSS JOIN sys.all_columns  c2
)
SELECT  
	CASE WHEN RN = 1 THEN StartDT  
		ELSE CAST(DATEADD(day, RN-1,CAST(StartDT as date) ) as datetime	)
	END NewStart
	,
	CASE WHEN RN = DATEDIFF(day, t1.StartDT,t1.EndDT)+1 
		THEN EndDT
	ELSE
		DATEADD(second,-1,	CAST(DATEADD(day, RN,CAST(StartDT as date)) as datetime))
	END EndDT

FROM #tmp t1
CROSS APPLY(SELECT RN from NUMS where RN &lt;= DATEDIFF(day, t1.StartDT,t1.EndDT)+1) DayNums
ORDER BY NEWStart

答案3

得分: 0

DECLARE @table AS TABLE (
startdate DATETIME,
enddate DATETIME
);
INSERT INTO @table
(
startdate,
enddate
)
VALUES
('2023-05-01', -- startdate - datetime
'2023-05-05' -- enddate - datetime
),
('2023-05-10', -- startdate - datetime
'2023-05-15' -- enddate - datetime
);
WITH cte
AS (
SELECT
startdate,
DATEADD(SECOND, 86399, startdate) enddate,
'N' AS msg,
enddate AS newendate ---column to stop loop
FROM @table
UNION ALL
SELECT
DATEADD(SECOND, 1, enddate) startdate,
DATEADD(SECOND, 86399, DATEADD(SECOND, 1, enddate)) AS enddate,
'Y' msg,
newendate
FROM cte
WHERE startdate < cte.newendate
)
SELECT
startdate,
enddate
FROM cte
ORDER BY 1;

英文:

I think below code can help you 填充缺失的日期,给定日期范围。

DECLARE @table AS TABLE (
                    startdate DATETIME,
                    enddate DATETIME
                    );
INSERT INTO @table
(
startdate,
enddate
)
VALUES
 (&#39;2023-05-01&#39;, -- startdate - datetime
  &#39;2023-05-05&#39;  -- enddate - datetime
),
 (&#39;2023-05-10&#39;, -- startdate - datetime
  &#39;2023-05-15&#39;  -- enddate - datetime
 );
WITH cte
 AS (
  SELECT
      startdate,
      DATEADD(SECOND, 86399, startdate) enddate,
      &#39;N&#39;                               AS msg,
      enddate                           AS newendate ---column to stop loop
 FROM @table
 UNION ALL
 SELECT
       DATEADD(SECOND, 1, enddate)                         startdate,
       DATEADD(SECOND, 86399, DATEADD(SECOND, 1, enddate)) AS enddate,
       &#39;Y&#39;                                                 msg,
       newendate
 FROM  cte
 WHERE startdate &lt; cte.newendate
 )
 SELECT
     startdate,
     enddate
 FROM     cte
 ORDER BY 1;

答案4

得分: 0

;WITH _list as (

    select 
             min(Start_) mins
            ,max(Start_) as maxs

    from TestA
), listTest AS
(
    select 
            mins as StartTime,iif(
                                        mins=maxs
                                    ,maxs,
                           
                                        cast( cast( cast( mins as date) as varchar(100)) +cast( ' 23:59:59' as varchar(100)) as datetime)
                            
                                ) 
                                as EndTime
                                ,mins,maxs
    from _list
    UNION ALL 
    SELECT         DATEADD(DAY,1, 
                                cast( cast( cast(  StartTime as date) as varchar(100)) 
                               +cast( ' 00:00:00' as varchar(100)) as datetime)
                            
    
    
                    ) AS StartTime,
                    IIF( 
                            cast( DATEADD(DAY,1, StartTime ) as date)= 
                            cast(maxs as date)
                            ,maxs,DATEADD(DAY,1, EndTime )
                    ) AS EndTime
                     ,mins,maxs
    FROM   listTest 
    WHERE  StartTime &lt; (select cast( maxs as date) from _list)

)
select  
            StartTime
           ,EndTime
from listTest a
left join TestA  b on cast( a.StartTime as date)=cast( b.Start_ as date)
英文:

You can Use Cte


;WITH _list as (

		select 
		         min(Start_) mins
				,max(Start_) as maxs

		from TestA
), listTest AS
(
    select 
			mins as StartTime,iif(
										mins=maxs
									,maxs,
						   
										cast( cast( cast( mins as date) as varchar(100)) +cast( &#39; 23:59:59&#39; as varchar(100)) as datetime)
							
								) 
								as EndTime
								,mins,maxs
	from _list
    UNION ALL 
	SELECT         DATEADD(DAY,1, 
	                            cast( cast( cast(  StartTime as date) as varchar(100)) 
					           +cast( &#39; 00:00:00&#39; as varchar(100)) as datetime)
							
	
	
					) AS StartTime,
					IIF( 
							cast( DATEADD(DAY,1, StartTime ) as date)= 
							cast(maxs as date)
							,maxs,DATEADD(DAY,1, EndTime )
					) AS EndTime
					 ,mins,maxs
	FROM   listTest 
    WHERE  StartTime &lt; (select cast( maxs as date) from _list)

)
select  
            StartTime
		   ,EndTime
from listTest a
left join TestA  b on cast( a.StartTime as date)=cast( b.Start_ as date)

Demo

huangapple
  • 本文由 发表于 2023年6月5日 18:12:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76405394.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定