Как определить, перекрываются ли друг с другом набор событий (или временных интервалов) в базе данных PostgreSQL - PullRequest
1 голос
/ 03 февраля 2020

Нам дан набор данных, в котором есть id, начальная и конечная точка для каждого события. Наша задача - определить, совпадает ли данное событие с каким-либо другим событием. Я попытался найти решение, используя временную таблицу, несколько соединений и условия случая. Было бы здорово, если бы люди могли поделиться более эффективным и лаконичным способом решения этой проблемы. Вот ссылка на решения.

Ввод:

id  Start_date  End_date

1   2019-01-01  2019-01-31
2   2019-01-15  2019-01-17
3   2019-01-29  2019-02-04
4   2019-02-05  2019-02-10

Окончательный вывод:

id       Overlap
1        True
2        True
3        True
4        False

Ответы [ 2 ]

3 голосов
/ 03 февраля 2020

В стандартном SQL это можно сделать с помощью коррелированного подзапроса:

select
    t.id,
    case 
        when exists (
            select 1
            from mytable t1
            where 
                t1.id <> t.id
                and t1.start_date <= t.end_date 
                and t1.end_date  >= t.start_date
        ) 
        then 'true'
        else 'false'
    end overlap
from mytable t

Коррелированный подзапрос, введенный условием exists, проверяет, имеет ли другая запись диапазон дат, перекрывающий текущую запись. ; некоторые базы данных имеют встроенные функции для этого (а именно, Postgres имеет оператор overlaps).

Некоторые базы данных также поддерживают прямую оценку условий как логических (или 0/1 чисел): в этом случае вам не нужно выражение case, вы можете непосредственно поместить условие exists в список select.


Редактировать : в Postgres, что было наконец помеченный, запрос становится проще:

select
    t.id,
    exists (
        select 1
        from mytable t1
        where 
            t1.id <> t.id
            and (t1.start_date, t1.end_date) overlaps (t.start_date, t.end_date)
    ) overlap
from mytable t

SQLFiddle

1 голос
/ 03 февраля 2020

Я бы порекомендовал использовать совокупные max() и min():

select t.*,
       (prev_ed >= start_date or next_sd <= end_date) as has_overlap
from (select t.*,
             max(end_date) over (order by start_date rows between unbounded preceding and 1 preceding) as prev_ed,
             min(start_date) over (order by start_date rows between 1 following and unbounded following) as next_sd
      from t
     ) t;

При этом используются оконные функции для получения максимальной даты окончания перед данной строкой - упорядочено по дата начала И чтобы получить минимальную дату начала после заданной строки с тем же порядком.

Затем он вычисляет перекрытия, сравнивая эти значения со значениями в строке.

...