Найти перекрывающиеся дни между двумя периодами даты - PullRequest
0 голосов
/ 24 сентября 2018

У меня есть две таблицы, каждая из которых содержит период дат (от даты1 до даты2)

Я найду перекрывающиеся дни между двумя периодами даты в таблице1 и таблице2

Пример

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

Ответы [ 3 ]

0 голосов
/ 24 сентября 2018

Одним из возможных решений является использование таблицы 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)

0 голосов
/ 24 сентября 2018

Это можно сделать с помощью 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
0 голосов
/ 24 сентября 2018

Это должно работать:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...