【问题标题】:Find missing date ranges between two dates查找两个日期之间缺失的日期范围
【发布时间】:2021-08-15 11:11:47
【问题描述】:

我有一个包含某些日期范围数据的表格。当用户选择开始日期和结束日期时,结果集将类似于这两个日期之间的所有日期范围以及这两个日期之间的所有缺失日期范围。
例如:
日期范围表

ID| fromdate  | todate    |
----------------------------
1 | 5-May-21  | 10-May-21 |
2 | 17-May-21 | 25-May-21 |

这是我的主表,我在下面提到了我想要的上表的所有结果集

如果用户选择: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 |

如果用户选择: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 |

如果用户选择: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?

提前致谢。

【问题讨论】:

  • 我没有共享代码的权限。但我做了这种逻辑:```声明@startdate datetime declare @enddate datetime set @startdate = '2021-04-24 00:00: 00.000' 设置@enddate = '2021-06-30 00:00:00.000' 插入@tempPeriod 选择ID , FromDate , ToDate , CID = lead(ID,1,null) over (Order By fromdate) from tablename where (FromDate >= @startdate 和 FromDate = @startdate 和 ToDate
  • edit 直接对问题进行任何澄清。我们不想看到实时代码,我们希望您使用测试数据和测试代码创建一个minimal reproducible example
  • 最后一个expect结果集看不懂;为什么第一行有一个1ID,而表中不存在这些日期,为什么最后一行有一个2ID 出于同样的原因?
  • @DaleK 这是我在这个网站上的第一个问题,我自己尝试这个。我明白你在说什么我会努力解决我的错误谢谢你的指导。
  • @Larnu,对不起,你错了,我在最后一个结果集上添加了 ID 错误的 ID,我想要虚拟创建的记录的 0 ID。谢谢

标签: sql sql-server date date-range between


【解决方案1】:

注意,我在这里假设您的最终预期结果的预期结果在此处错误,因为它与其他不匹配 2. 最后一个和第一个最后一组的预期结果中的行都具有ID 的值,而不是0,但没有给出他们这样做的为什么的解释。因此,我假设该值应该是0,就像“中间”中的行一样。

为此,我使用一个 Tally 来获取您需要的日期范围之间的所有日期; Tally 限制为 1,000 行,略低于 3 年,但如果您需要更多行,可以交叉加入 N 更多。然后我使用该计数来创建一个内联日历表。接下来我 LEFT JOIN 将该日历添加到您的数据中,并使用间隙和孤岛方法将值分组。最后,我汇总这些组,在每个组中获取 MINMAX 日期:

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;
                          

db<>fiddle

【讨论】:

  • 感谢您的快速回复。这对我有用,谢谢。
【解决方案2】:

您可以为此使用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 是一个 dbfiddle。

【讨论】:

    猜你喜欢
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多