如果我有一张像这样的桌子
begin date end date data
2013-01-01 2013-01-04 7
2013-01-05 2013-01-06 9
这样退货怎么办...
date data
2013-01-01 7
2013-01-02 7
2013-01-03 7
2013-01-04 7
2013-01-05 9
2013-01-06 9
我想做的一件事是有另一个只有所有日期的表,然后使用
date>=begin date
和 date<=end date
将只有日期的表连接到上表,但这似乎有点笨拙,必须保持额外的表格只有重复的日期。
在某些情况下,我没有数据范围,只有
as of
日期,这基本上看起来像我的第一个示例,但没有 end date
。 end date
由下一行的“截至”日期暗示(即结束日期应为下一行的as of
-1)。我对此有一个“解决方案”,它使用 row_number() 函数来获取下一个值,但我怀疑这种方法(我这样做的方式有一堆嵌套的自连接)会导致查询时间很长。
使用一些示例数据...
create table data (begindate datetime, enddate datetime, data int);
insert data select
'20130101', '20130104', 7 union all select
'20130105', '20130106', 9;
查询:(注意:如果您已经有一个数字/理货表 - 使用它)
select dateadd(d,v.number,d.begindate) adate, data
from data d
join master..spt_values v on v.type='P'
and v.number between 0 and datediff(d, begindate, enddate)
order by adate;
结果:
| COLUMN_0 | DATA |
-----------------------------------------
| January, 01 2013 00:00:00+0000 | 7 |
| January, 02 2013 00:00:00+0000 | 7 |
| January, 03 2013 00:00:00+0000 | 7 |
| January, 04 2013 00:00:00+0000 | 7 |
| January, 05 2013 00:00:00+0000 | 9 |
| January, 06 2013 00:00:00+0000 | 9 |
或者,您可以动态生成一个数字表 (0-99) 或根据需要生成任意数量的数字
;WITH Numbers(number) AS (
select top(100) row_number() over (order by (select 0))-1
from sys.columns a
cross join sys.columns b
cross join sys.columns c
cross join sys.columns d
)
select dateadd(d,v.number,d.begindate) adate, data
from data d
join Numbers v on v.number between 0 and datediff(d, begindate, enddate)
order by adate;
您可以使用递归 CTE 获取两个日期之间的所有日期。另一个 CTE 是获取 ROW_NUMBER 来帮助您处理那些缺少的 EndDates。
DECLARE @startDate DATE
DECLARE @endDate DATE
SELECT @startDate = MIN(begindate) FROM Table1
SELECT @endDate = MAX(enddate) FROM Table1
;WITH CTE_Dates AS
(
SELECT @startDate AS DT
UNION ALL
SELECT DATEADD(DD, 1, DT)
FROM CTE_Dates
WHERE DATEADD(DD, 1, DT) <= @endDate
)
,CTE_Data AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY BeginDate) AS RN FROM Table1
)
SELECT DT, t1.data FROM CTE_Dates d
LEFT JOIN CTE_Data t1 on d.DT
BETWEEN t1.[BeginDate] AND COALESCE(t1.EndDate,
(SELECT DATEADD(DD,-1,t2.BeginDate) FROM CTE_Data t2 WHERE t1.RN + 1 = t2.RN))
DECLARE @tblDateRange TABLE (
Id INT,
FromDateTime DATETIME,
ToDateTime DATETIME
)
INSERT INTO @tblDateRange (Id, FromDateTime, ToDateTime)
VALUES
(1, '2023-01-01 10:00:00.000', '2023-01-01 10:00:00.000'), --starts and ends in same
(2, '2023-02-02 00:00:00.000', '2023-02-04 23:59:59.000'), --starts and ends in standered
(3, '2023-03-05 10:00:00.000', '2023-03-06 23:59:59.000'), --starts in middle of another date
(4, '2023-04-07 00:00:00.000', '2023-04-08 21:00:00.000'), --ends in middle of another date
(5, '2023-05-09 11:00:00.000', '2023-05-11 11:00:00.000'), --starts and ends in middle of another date
(6, '2023-06-01 10:00:00.000', '2023-06-01 22:00:00.000'); --starts and ends in middle of same date
WITH DateRanges(Id, FromDateTime, ToDateTime, LevelNo, [Date], DateWiseStartDateTime, DateWiseEndDateTime)
AS
(
SELECT
p.*,
1,
CAST(p.FromDateTime AS DATE),
p.FromDateTime,
IIF(DATEADD(SECOND, -1, DATEADD(DAY, DATEDIFF(DAY, 0, p.FromDateTime) + 1, 0)) < p.ToDateTime, DATEADD(SECOND, -1, DATEADD(DAY, DATEDIFF(DAY, 0, p.FromDateTime) + 1, 0)), p.ToDateTime)
FROM @tblDateRange p
UNION ALL
SELECT
c.*,
p.LevelNo + 1,
CAST(DATEADD(DAY, DATEDIFF(DAY, 0, DATEADD(DAY, 1, p.DateWiseStartDateTime)), 0) AS DATE),
DATEADD(DAY, DATEDIFF(DAY, 0, DATEADD(DAY, 1, p.DateWiseStartDateTime)), 0),
IIF(c.ToDateTime < DATEADD(DAY, 1, p.DateWiseEndDateTime), c.ToDateTime, DATEADD(DAY, 1, p.DateWiseEndDateTime))
FROM @tblDateRange c
JOIN DateRanges p ON c.Id = p.Id
WHERE DATEADD(DAY, DATEDIFF(DAY, 0, DATEADD(DAY, 1, p.DateWiseStartDateTime)), 0) < c.ToDateTime --date wise start datetime < range to datetime
)
SELECT *
FROM DateRanges
ORDER BY Id, LevelNo
OPTION (MAXRECURSION 30000) --max 32767, no limit 0