Каков простой и эффективный способ найти строки с перекрытиями временных интервалов в SQL? - PullRequest
26 голосов
/ 23 сентября 2008

У меня есть две таблицы, обе с полями времени начала и окончания. Мне нужно найти для каждой строки в первой таблице все строки во второй таблице, где пересекаются временные интервалы.

Например:

           <-----row 1 interval------->
<---find this--> <--and this--> <--and this-->

Пожалуйста, сформулируйте свой ответ в форме SQL WHERE -пункта И рассмотрите случай, когда время окончания во второй таблице может быть NULL.

Целевой платформой является SQL Server 2005, но могут также представлять интерес решения других платформ.

Ответы [ 3 ]

52 голосов
/ 23 сентября 2008
SELECT * 
FROM table1,table2 
WHERE table2.start <= table1.end 
AND (table2.end IS NULL OR table2.end >= table1.start)
2 голосов
/ 23 сентября 2008
select * from table_1 
right join 
table_2 on 
(
table_1.start between table_2.start and table_2.[end]
or
table_1.[end] between table_2.start and table_2.[end]
or
(table_1.[end] > table_2.start and table_2.[end] is null)
)

РЕДАКТИРОВАТЬ: Хорошо, не идите на мое решение, оно выглядит как дерьмо. Решение «где» в 14 раз быстрее. К сожалению ...

Немного статистики: работа с БД с ~ 65000 записей для таблиц 1 и 2 (без индексации), с интервалами в 2 дня между началом и концом для каждой строки, работа в SQLSMSE в течение 2 минут (без терпение ждать)

Использование объединения: 8356 строк за 2 минуты

Использование где: 115436 строк за 2 минуты

1 голос
/ 26 июня 2017

Звучит очень сложно, пока вы не начнете работать с реверса. Ниже я проиллюстрировал только хорошие случаи (без наложений)! определенные этими двумя простыми условиями, у нас нет перекрывающихся диапазонов, если condA ИЛИ condB ИСТИНА, поэтому мы собираемся обратить эти НЕ condA И НЕ CondB, в нашем случае я просто поменял местами знаки (> стал <=) </p>

/*
|--------| A                             \___  CondA: b.ddStart >  a.ddEnd
            |=========| B                /      \____ CondB:  a.ddS >  b.ddE
                          |+++++++++| A         /
*/
--DROP TABLE ran
create table ran ( mem_nbr int, ID int, ddS date, ddE date)
insert ran values  
(100, 1,  '2012-1-1','2012-12-30'),    ----\ ovl
(100, 11, '2012-12-12','2012-12-24'),  ----/
(100, 2, '2012-12-31','2014-1-1'),
(100, 3, '2014-5-1','2014-12-14') ,

(220, 1, '2015-5-5','2015-12-14') ,    ---\ovl
(220, 22, '2014-4-1','2015-5-25') ,    ---/
(220, 3, '2016-6-1','2016-12-16')  

select  DISTINCT a.mem_nbr ,  a.* , '-' [ ], b.dds, b.dde, b.id 
FROM ran a
join ran b  on  a.mem_nbr = b.mem_nbr          -- match by mem#
               AND     a.ID <> b.ID            -- itself
                  AND     b.ddS <= a.ddE        -- NOT    b.ddS >  a.ddE       
                  AND     a.ddS <= b.ddE        -- NOT    a.ddS >  b.ddE   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...