查找两个日期之间缺失的日期范围
Find missing date ranges between two dates
我 table 有一些日期范围的数据。当用户 select 开始日期和结束日期时,结果集将类似于这 2 个日期之间的所有日期范围以及这 2 个日期之间的所有缺失日期范围。
例如:
DateRangesTable
ID| fromdate | todate |
----------------------------
1 | 5-May-21 | 10-May-21 |
2 | 17-May-21 | 25-May-21 |
这是我的主要 table ,我在下面提到了我想要的所有结果集 table
如果用户 select:2021 年 5 月 5 日 至 2021 年 5 月 25 日
预期结果:
ID| fromdate | todate |
----------------------------
1 | 5-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 25-May-21 |
如果用户 select:2021 年 5 月 6 日 到 2021 年 5 月 23 日
预期结果:
ID| fromdate | todate |
-----------------------------
1 | 6-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 23-May-21 |
如果用户 select:2021 年 5 月 1 日 至 2021 年 5 月 28 日
预期结果:
ID| fromdate | todate |
----------------------------
1 | 1-May-21 | 4-May-21 |
1 | 5-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 25-May-21 |
2 | 26-May-21 | 28-May-21 |
这里有一些不相似但尝试找到的问题:
SQL Find missing date ranges
SQL how to write a query that return missing date ranges?
提前致谢。
请注意,我在这里假设您最终预期结果的预期结果是错误的,因为它不匹配另一个 2. 最后一组预期结果中的最后一行和第一行都有一个 ID
的值,它不是 0
,但没有解释 为什么 [=29] =] 他们做的是给定的。因此,我 假设 该值应该是 0
就像“中间”中的行一样。
为此,我使用 Tally 来获取您需要的日期范围内的所有日期; Tally 限制为 1,000 行,还差 3 年,但如果您需要更多行,可以交叉连接到 N
。然后我使用该计数创建一个内联日历 table。接下来,我 LEFT JOIN
将日历添加到您的数据中,并使用间隙和岛方法将值分组。最后,我然后汇总这些组,在每个组中获取 MIN
和 MAX
日期:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (ID int,
FromDate date,
ToDate date);
INSERT INTO dbo.YourTable
VALUES(1,'20210505','20210510'),
(2,'20210517','20210525');
GO
DECLARE @StartDate date = '20210501',
@EndDate date = '20210528';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --1000 days
Dates AS(
SELECT DATEADD(DAY, T.I, @StartDate) AS [Date],
T.I
FROM Tally T),
Grps AS(
SELECT D.[Date],
YT.ID,
D.I - ROW_NUMBER() OVER (PARTITION BY ID ORDER BY D.[Date]) AS Grp
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.[Date] >= YT.FromDate AND D.[Date] <= YT.ToDate)
SELECT ISNULL(MAX(G.ID),0) AS ID,
MIN(G.[Date]) AS FromDate,
MAX(G.[Date]) AS ToDate
FROM Grps G
GROUP BY G.Grp
ORDER BY FromDate ASC;
GO
DROP TABLE dbo.YourTable;
您可以为此使用 union all
:
-- first get the existing rows
select id,
(case when fromdate < @fromdate then @fromdate else fromdate end) as fromdate,
(case when todate > @todate then @todate else todate end) as to_date
from t
where fromdate < @todate and
todate > @fromdate
union all
-- then the in-between rows
select 0, dateadd(day, 1, todate) as fromdate, next_fromdate as todate
from (select t.*,
dateadd(day, -1, lead(fromdate) over (order by fromdate)) as next_fromdate
from t
) t
where fromdate >= @fromdate and todate <= @todate and
next_todate is not null
union all
-- then the earliest record, if any
select 0, @fromdate, min(fromdate)
from t
where todate > @fromdate
having @fromdate < min(fromdate)
union all
-- then the final record, if any
select 0, max(todate), @todate
from t
where fromdate < @todate
having @todate > max(todate);
Here 是一个 db<>fiddle.
我 table 有一些日期范围的数据。当用户 select 开始日期和结束日期时,结果集将类似于这 2 个日期之间的所有日期范围以及这 2 个日期之间的所有缺失日期范围。
例如:
DateRangesTable
ID| fromdate | todate |
----------------------------
1 | 5-May-21 | 10-May-21 |
2 | 17-May-21 | 25-May-21 |
这是我的主要 table ,我在下面提到了我想要的所有结果集 table
如果用户 select:2021 年 5 月 5 日 至 2021 年 5 月 25 日
预期结果:
ID| fromdate | todate |
----------------------------
1 | 5-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 25-May-21 |
如果用户 select:2021 年 5 月 6 日 到 2021 年 5 月 23 日
预期结果:
ID| fromdate | todate |
-----------------------------
1 | 6-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 23-May-21 |
如果用户 select:2021 年 5 月 1 日 至 2021 年 5 月 28 日
预期结果:
ID| fromdate | todate |
----------------------------
1 | 1-May-21 | 4-May-21 |
1 | 5-May-21 | 10-May-21 |
0 | 11-May-21 | 16-May-21 |
2 | 17-May-21 | 25-May-21 |
2 | 26-May-21 | 28-May-21 |
这里有一些不相似但尝试找到的问题:
SQL Find missing date ranges
SQL how to write a query that return missing date ranges?
提前致谢。
请注意,我在这里假设您最终预期结果的预期结果是错误的,因为它不匹配另一个 2. 最后一组预期结果中的最后一行和第一行都有一个 ID
的值,它不是 0
,但没有解释 为什么 [=29] =] 他们做的是给定的。因此,我 假设 该值应该是 0
就像“中间”中的行一样。
为此,我使用 Tally 来获取您需要的日期范围内的所有日期; Tally 限制为 1,000 行,还差 3 年,但如果您需要更多行,可以交叉连接到 N
。然后我使用该计数创建一个内联日历 table。接下来,我 LEFT JOIN
将日历添加到您的数据中,并使用间隙和岛方法将值分组。最后,我然后汇总这些组,在每个组中获取 MIN
和 MAX
日期:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (ID int,
FromDate date,
ToDate date);
INSERT INTO dbo.YourTable
VALUES(1,'20210505','20210510'),
(2,'20210517','20210525');
GO
DECLARE @StartDate date = '20210501',
@EndDate date = '20210528';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --1000 days
Dates AS(
SELECT DATEADD(DAY, T.I, @StartDate) AS [Date],
T.I
FROM Tally T),
Grps AS(
SELECT D.[Date],
YT.ID,
D.I - ROW_NUMBER() OVER (PARTITION BY ID ORDER BY D.[Date]) AS Grp
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.[Date] >= YT.FromDate AND D.[Date] <= YT.ToDate)
SELECT ISNULL(MAX(G.ID),0) AS ID,
MIN(G.[Date]) AS FromDate,
MAX(G.[Date]) AS ToDate
FROM Grps G
GROUP BY G.Grp
ORDER BY FromDate ASC;
GO
DROP TABLE dbo.YourTable;
您可以为此使用 union all
:
-- first get the existing rows
select id,
(case when fromdate < @fromdate then @fromdate else fromdate end) as fromdate,
(case when todate > @todate then @todate else todate end) as to_date
from t
where fromdate < @todate and
todate > @fromdate
union all
-- then the in-between rows
select 0, dateadd(day, 1, todate) as fromdate, next_fromdate as todate
from (select t.*,
dateadd(day, -1, lead(fromdate) over (order by fromdate)) as next_fromdate
from t
) t
where fromdate >= @fromdate and todate <= @todate and
next_todate is not null
union all
-- then the earliest record, if any
select 0, @fromdate, min(fromdate)
from t
where todate > @fromdate
having @fromdate < min(fromdate)
union all
-- then the final record, if any
select 0, max(todate), @todate
from t
where fromdate < @todate
having @todate > max(todate);
Here 是一个 db<>fiddle.