Как получить отдельные интервалы от перекрывающихся дат - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть таблица с датами начала и окончания, например, ниже

drop table if exists #Temp
select * 
into #Temp
from (values (1, 1, '2011-10-09', '2011-10-20'),
             (2, 1, '2011-10-14', '2011-10-30'),
             (3, 1, '2012-05-21', '2012-05-25')
     ) as value (id, userId, d1, d2)

, которая дает мне эту исходную таблицу:

id  userId    d1            d2
1   1         2011-10-09    2011-10-20
2   1         2011-10-14    2011-10-30
3   1         2012-05-21    2012-05-25

Проблема : я пытаюсь добиться того, чтобы получить 3 интервала из этих двух перекрывающихся дат.Как этого добиться?

select * 
from 
    #Temp t1
    join #Temp t2
        on t1.userId = t2.userId
        and t1.id != t2.id
where
    t1.d1 <= t2.d2
    and t1.d2 >= t2.d1
order by t1.d1

дает мне

id  userId    d1            d2              id  userId    d1            d2
1   1         2011-10-09    2011-10-20      2   1         2011-10-14    2011-10-30
2   1         2011-10-14    2011-10-30      1   1         2011-10-09    2011-10-20  

Я не уверен, как продолжить отсюда.

Что я хочу достичь, это следующеетаблица, где разделены перекрытия.

Ожидаемый результат:

userId  d1          d2              
1       2011-10-09  2011-10-14
1       2011-10-14  2011-10-20
1       2011-10-20  2011-10-30
1       2012-05-21  2012-05-25

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Это уродливый зверь, я должен признать.И данный ответ на 100% лучше.Но, тем не менее, это дает желаемый результат:

SELECT * 
INTO #Temp
FROM (VALUES (1, 1, '2011-10-09', '2011-10-20'),
             (2, 1, '2011-10-14', '2011-10-30'),
             (3, 1, '2012-05-21', '2012-05-25')
     ) AS VALUE (id, userId, d1, d2)

WITH CTE AS 
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY d1, d2) AS RowNumb
    FROM #Temp
)

SELECT C.*, 
      C2.d1 AS C2D1,
      C2.D2 AS C2D2,
      CASE WHEN CAST (C2.d1 AS DATETIME ) < CAST (C.d2 AS DATETIME) THEN 1 ELSE 0 END AS InsertField 
INTO #NewTable
FROM CTE AS C
LEFT JOIN CTE AS C2 ON C.RowNumb = C2.RowNumb - 1

SELECT * FROM #NewTable

SELECT id, userId, d1, CASE WHEN CAST (d2 AS DATETIME) < CAST (ISNULL (C2D1, '29000101') AS DATETIME) THEN d2 ELSE C2D1 END AS d2
INTO #Results
FROM #NewTable
UNION ALL
SELECT id, userid, C2D1, d2
FROM #NewTable
WHERE InsertField = 1

SELECT * FROM #NewTable

SELECT *, ROW_NUMBER() OVER (ORDER BY d1, d2) AS RowNumb 
INTO #R
FROM #Results AS R

SELECT R.id,
      R.userId,
      CASE WHEN CAST (R.d1 AS DATETIME) < CAST (R2.d2 AS DATETIME) THEN R2.d2 ELSE R.d1 END AS D1,
      R.d2
FROM #R AS R
LEFT JOIN #R AS R2 ON R.RowNumb = R2.RowNumb + 1
0 голосов
/ 07 декабря 2018

Один из способов - отменить сводку данных, затем упорядочить даты, а затем снова повернуть их, исключая некоторые записи.

WITH dataSource AS
(
    select id, userId, d1
    from #Temp t1
    UNION ALL
    select id, userId, d2
    from #Temp t1
), DataSourceOrdered AS
(
    SELECT 
        id, userId, d1,
        row_number () OVER (PARTITION BY userId ORDER By d1) as rowid
    FROM dataSource DS1
)
SELECT *
FROM DataSourceOrdered ds1
INNER JOIN DataSourceOrdered ds2
    ON ds1.userId = ds2.[userId]
    and ds1.[rowid] + 1 = ds2.[rowid]
    and exists (
        SELECT 1 
        FROM #Temp x 
        WHERE ds1.[d1] >= x.d1 and ds1.[d1] < x.d2 and ds1.userId = x.userId
    )

Возможно, потребуется немного изменить их в зависимости от ваших реальных данных,но с текущим, похоже, работает.

enter image description here

...