【问题标题】:Find number of days that intersect a given date range in a table of date ranges在日期范围表中查找与给定日期范围相交的天数
【发布时间】:2019-09-23 05:05:16
【问题描述】:

我想查找与日期范围表重叠的日期范围内的总天数。

例如,我在下表中的 2 个日期之间有 7 天。我想找到它们之间也属于这个日期范围的日子:2019-08-01 到 2019-08-30。

它应该返回 1 天。

这是数据源查询:

SELECT LeaveId, UserId, StartDate, EndDate, Days
FROM TblLeaveRequest
WHERE UserId = 218
LeaveID     UserID      StartDate               EndDate                 Days
----------- ----------- ----------------------- ----------------------- -----------
22484       218         2019-07-26 00:00:00.000 2019-08-01 00:00:00.000 7

【问题讨论】:

  • 如果您的数据无法做到这一点怎么办,例如如果2019-07-26 有两天的数据怎么办?
  • 提供一些样本数据,预期输出
  • 您的日期范围总是一个月吗?这会更容易,您不必指定要比较的范围的开始日期和结束日期。
  • 这可以通过多种方式回答,具体取决于您输入的内容。努力解释问题以获得最佳答案。您的输入是什么样的?你在 Proc.This is the data source query 之后传递什么东西没用。

标签: sql sql-server


【解决方案1】:

我相信这可能会对您有所帮助:

--create the table
SELECT 
    22484 LeaveID, 
    218 UserID, 
    CONVERT(DATETIME,'7/26/2019') StartDate, 
    CONVERT(DATETIME,'8/1/2019') EndDate, 
    7 Days
INTO #TblLeaveRequest

--Range Paramters
DECLARE @StartRange AS DATETIME = '8/1/2019'
DECLARE @EndRange AS DATETIME = '8/30/2019'

--Find sum of days between StartDate and EndDate that intersect the range paramters
--for UserId=218
SELECT 
    SUM(
    DATEDIFF(
        DAY
        ,CASE WHEN @StartRange < StartDate THEN StartDate ELSE @StartRange END
        ,DATEADD(DAY, 1, CASE WHEN @EndRange > EndDate THEN EndDate ELSE @EndRange END)
        )
    ) TotalDays
from #TblLeaveRequest
where UserId=218

这假定没有开始日期大于结束日期。它还假定范围参数始终与表中范围的某些部分相交。

如果参数范围可能不相交,那么您必须通过排除负数来消除这些情况:

SELECT SUM( CASE WHEN Days < 0 THEN 0 ELSE Days END ) TotalDays
FROM
(
SELECT 
    DATEDIFF(
        DAY
        ,CASE WHEN @StartRange < StartDate THEN StartDate ELSE @StartRange END
        ,DATEADD(DAY, 1, CASE WHEN @EndRange > EndDate THEN EndDate ELSE @EndRange END)
        ) Days
from #TblLeaveRequest
where UserId=218
) TotalDays

【讨论】:

  • 如果表中的某些行具有不与给定范围相交的区间,则查询中的DATEDIFF 将返回负值,该负值将与SUM 相加。也许这不是@rinkuChoudhary 想要看到的结果。
  • 好点...我已在预期该案例的答案中添加了一个查询。
【解决方案2】:

如果我正确理解您的问题,您有两个日期范围,并且您正在寻找交叉点的天数。

考虑甘特图:

Start_1 .................... End_1
                 Start_2 .......................End_2

如果你可以创建一个类似的表结构

LeaveID     UserID      StartDate_1  EndDate_1  StartDate_2  EndDate_2 
----------- ----------- ----------   ---------  ----------   ---------
22484       218         2019-07-26  2019-08-01  2019-08-01   2019-08-30 

您可以通过

来确定天数
select 
 leaveID
 , UserID
 ,case 
      when StartDate_2 <= EndDate_1 then datediff(day,StartDate_2,EndDate_1) + 1
      else 0
      end as delta_days_intersection
from table

希望对你有帮助

【讨论】:

    【解决方案3】:

    您可以使用数字(Tally)表来计算天数:

    SQL Fiddle

    MS SQL Server 2017 架构设置:

    CREATE TABLE LeaveRequest
    (
      LeaveId Int,
      UserId Int,
      StartDate Date,
      EndDate Date,
      Days Int
    )
    
    Insert Into LeaveRequest
    VALUES
    (22484, 218, '2019-07-26','2019-08-01', 7)
    

    查询 1:

    DECLARE @StartDate Date = '2019-08-01'
    DECLARE @EndDate Date = '2019-08-30'
    ;WITH Tally
    AS
    (
      SELECT ROW_NUMBER() OVER (ORdER By Numbers.Num) AS Num
      FROM
      (
        Values(1),(2),(3),(4),(5),(6),(7),(8),(9)
      )Numbers(Num)
      Cross APPLY
      (
        Values(1),(2),(3),(4),(5),(6),(7),(8),(9)
      )Numbers2(Num2)
    )
    SELECT COUNT(DATEADD(d, Num -1, StartDate)) As NumberOfDays
    FROM LeaveRequest
    CROSS APPLY Tally
    WHERE  DATEADD(d, Num -1, StartDate) <= EndDate AND
    DATEADD(d, Num -1, StartDate) >= @StartDate AND
    DATEADD(d, Num -1, StartDate) <= @EndDate
    

    Results:

    | NumberOfDays |
    |--------------|
    |            1 |
    

    【讨论】:

      【解决方案4】:
      CREATE TABLE #LeaveRequest
      (
        LeaveId Int,
        UserId Int,
        StartDate Date,
        EndDate Date,
        Days Int
      )
      
      Insert Into #LeaveRequest
      VALUES
      (22484, 218, '2019-07-26','2019-08-01', 7)
      
      Declare @FromDate datetime='2019-08-01' 
      declare @ToDate datetime='2019-08-30'
      
      
      
      ;With CTE as
      (
      select top (DATEDIFF(day,@FromDate,@ToDate)+1) 
      DATEADD(day, ROW_NUMBER()over(order by (select null))-1,@FromDate) DT
      from sys.objects
      )
      
      select * from #LeaveRequest LR
      cross apply(select count(*)IntersectingDays 
      from CTE c where dt between lr.StartDate and lr.EndDate)ca
      
      --or
      --select lr.*,c.* 
      from  CTE c 
      --cross apply
      (select lr.* from #LeaveRequest LR 
      where c.DT between lr.StartDate and lr.EndDate)lr
      
      drop table #LeaveRequest
      

      最好创建一个日历表,它也总是可以帮助您处理其他查询。

      create table CalendarDate(Dates DateTime primary key)
      insert into CalendarDate WITH (TABLOCK)  (Dates)
      select top (1000000) 
      DATEADD(day, ROW_NUMBER()over(order by (select null))-1,'1970-01-01') DT
      from sys.objects
      

      然后在CTE里面写这个,

      select top (DATEDIFF(day,@FromDate,@ToDate)+1) Dates from CalendarDate
      where Dates between @FromDate and @ToDate
      

      【讨论】:

        【解决方案5】:
        DECLARE @FromDate datetime = '2019-08-01' 
        DECLARE @ToDate datetime = '2019-08-30'
        
        SELECT
            IIF(@FromDate <= EndDate AND @ToDate >= StartDate,
                DATEDIFF(day,
                    IIF(StartDate > @FromDate, StartDate, @FromDate),
                    IIF(EndDate < @ToDate, EndDate, @ToDate)
                ),
            0) AS overlapping_days
        FROM TblLeaveRequest;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-20
          • 1970-01-01
          • 2014-03-29
          • 1970-01-01
          • 2015-03-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多