【问题标题】:Find overlapping days between two periods of Date查找两个日期期间之间的重叠天数
【发布时间】:2018-09-24 14:22:36
【问题描述】:

我有两个表,每个表都包含日期期间(从 date1 到 date2)

我将在 table1 和 table2 中的两个 Date 期间之间查找重叠的天数

例子

table1
-------------------------
id   |  FromDate | ToDate
1    |2000-01-01 | 2000-02-04
2    |2000-03-01 | 2000-03-29

table2
-------------------------
id   | FromDate  | ToDate
1    |2000-02-01 | 2000-02-07
2    |2000-03-27 | 2000-03-29

我想要的结果:

2000-02-01
2000-02-02
2000-02-03
2000-02-04
2000-03-27
2000-03-28
2000-03-29

【问题讨论】:

  • 单个表中的期间是否可以重叠,例如一月的第一周和一月?表格之间是否可以重叠两行以上,例如一月的第一周和最后一周在table1 和一月份在table2?你试过什么?

标签: sql-server tsql


【解决方案1】:

这应该可行:

CREATE TABLE #t1 
(
  id int,
  FromDate date,
  ToDate date
)
CREATE TABLE #t2
(
  id int,
  FromDate date,
  ToDate date
)

INSERT #t1 VALUES 
(1, '2000-01-01', '2000-02-04'),
(2, '2000-03-01', '2000-03-29')

INSERT #t2 VALUES 
(1, '2000-02-01', '2000-02-07'),
(2, '2000-03-27', '2000-03-29')

WITH DateRange AS --select range where intersection is possible
(
    SELECT MAX(MinDate) MinDate,MIN(MaxDate) MaxDate,DATEDIFF(DAY,MAX(MinDate),MIN(MaxDate)) Diff
    FROM (VALUES ((SELECT MIN(FromDate) FROM #t1)),((SELECT MIN(FromDate) FROM #t2))) MinDate(MinDate)
    CROSS APPLY (VALUES ((SELECT MAX(ToDate) FROM #t1)),((SELECT MAX(ToDate) FROM #t2))) MaxDate(MaxDate)
), AllDates AS --generate sequence of days
(
    SELECT MinDate D, MaxDate Limit
    FROM DateRange
    UNION ALL
    SELECT DATEADD(DAY, 1, D), Limit
    FROM AllDates
    WHERE DATEADD(DAY, 1, D)<=Limit
) --select all days existing in any range in both tables
SELECT D
FROM AllDates
WHERE EXISTS (SELECT * FROM #t1 WHERE D>=FromDate AND D<=ToDate)
  AND EXISTS (SELECT * FROM #t2 WHERE D>=FromDate AND D<=ToDate)

【讨论】:

    【解决方案2】:

    使用 CTE 和递归可以做到这一点。

    --Your sample data
    DECLARE @table1 TABLE (id int PRIMARY KEY, FromDate date, ToDate date)
    DECLARE @table2 TABLE (id int PRIMARY KEY, FromDate date, ToDate date)
    INSERT INTO @table1 VALUES (1, '2000-01-01', '2000-02-04') , (2, '2000-03-01', '2000-03-29')
    INSERT INTO @table2 VALUES (1, '2000-02-01', '2000-02-07') , (2, '2000-03-27', '2000-03-29')
    
    --A couple CTE's
    ;WITH cteDates AS (        
    SELECT T1.id --get the min and max dates for each id
          ,CASE WHEN T1.FromDate > T2.FromDate THEN T1.FromDate ELSE T2.FromDate END [mindate]
          ,CASE WHEN T1.ToDate < T2.ToDate THEN T1.ToDate ELSE T2.ToDate END [maxdate]    
      FROM @table1 T1 INNER JOIN @table2 T2 ON T1.id = T2.id
    )
    
    , cteRecursion AS ( --date range for each id
    SELECT id, mindate AS DateValue
      FROM cteDates
    
    UNION ALL
    
    SELECT id, DATEADD(DAY, 1, DateValue)
      FROM cteRecursion C1
     WHERE DATEADD(DAY, 1, DateValue) <= (
                                           SELECT maxDate 
                                             FROM cteDates C2
                                            WHERE C2.id = C1.id
                                         )
    )
    
    --SELECT query
    SELECT DateValue FROM cteRecursion ORDER BY DateValue OPTION (MAXRECURSION 0)
    

    产生输出:

    DateValue
    ---------
    2000-02-01
    2000-02-02
    2000-02-03
    2000-02-04
    2000-03-27
    2000-03-28
    2000-03-29
    

    【讨论】:

      【解决方案3】:

      一种可能的解决方案是使用 Numbers 或 Tally 表

      ;WITH cteNumbers (N) 
      AS(   
          SELECT ROW_NUMBER() OVER(ORDER BY N1.N) 
          FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N1(N)
          CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N2 (N)
          CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N3 (N)
      )
      SELECT T1.FromDate
      FROM(
          SELECT
              T1.FromDate
          FROM dbo.Table1 T1
          UNION
          SELECT
              DATEADD(DAY, N, T1.FromDate)
          FROM
              dbo.Table1 T1
          CROSS APPLY cteNumbers N
          WHERE N <= DATEDIFF(DAY, T1.FromDate, T1.ToDate)
      ) T1
      WHERE t1.FromDate IN 
      (
          SELECT
              T2.FromDate
          FROM dbo.Table2 T2 
          UNION 
          SELECT
              DATEADD(DAY, N, T2.FromDate)
          FROM
              dbo.Table2 T2
          CROSS APPLY cteNumbers N
          WHERE N <= DATEDIFF(DAY, T2.FromDate, T2.ToDate)
      ) 
      

      结果是

      FromDate
      2000-02-01 00:00:00.000
      2000-02-02 00:00:00.000
      2000-02-03 00:00:00.000
      2000-02-04 00:00:00.000
      2000-03-27 00:00:00.000
      2000-03-28 00:00:00.000
      2000-03-29 00:00:00.000
      

      Numbers/tally 表将允许最多 1000 天的日期范围。如果您需要更多,请像这样添加另一行,CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N4 (N)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-05
        • 1970-01-01
        • 1970-01-01
        • 2021-06-19
        • 2017-02-25
        • 2020-07-19
        • 1970-01-01
        相关资源
        最近更新 更多