Как найти перекрывающиеся временные срезы серверных элементов ключ-значение

#sql #sql-server

#sql #sql-сервер

Вопрос:

Я хотел бы узнать, есть ли у меня перекрывающиеся временные срезы с одинаковым идентификатором и одинаковым именем. В следующем примере записи с id = 2 и name = c перекрываются. Запись с id = 1 предназначена только для демонстрации хорошего примера.

Данная таблица:

  --- ------ ------- ------------ -------------- 
|id | name | value | validFrom  |  validTo     |
 --- ------ ------- ------------ -------------- 
|1  | a    | 12    | 2019-01-01 |  9999-12-31  |
|1  | b    | 34    | 2019-01-01 |  2019-10-31  |
|1  | b    | 35    | 2019-11-01 |  9999-12-31  |
|1  | c    | 13    | 2019-01-01 |  2025-12-31  |
|2  | a    | 49    | 2019-01-01 |  9999-12-31  |
|2  | b    | 99    | 2019-01-01 |  2034-12-31  |
|2  | c    | 75    | 2019-01-01 |  2019-10-31  |
|2  | c    | 84    | 2019-10-28 |  9999-12-31  |
|n  | ...  | ...   | ...        |  ...         |
 --- ------ ------- ------------ -------------- 
  

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

  --- ------ 
|id | name |
 --- ------ 
|2  | c    |
 --- ------ 
  

Заранее спасибо за помощь!

Ответ №1:

Вы можете получить перекрывающиеся строки, используя exists :

 select t.*
from t
where exists (select 1
              from t t2
              where t2.id = t.id and
                    t2.name = t.name and
                    t2.value <> t.value and
                    t2.validTo > t.validFrom and
                    t2.validFrom < t.validTo
             );
  

Если вам просто нужны id name комбинации /:

 select distinct t.id, t.name
from t
where exists (select 1
              from t t2
              where t2.id = t.id and
                    t2.name = t.name and
                    t2.value <> t.value and
                    t2.validTo > t.validFrom and
                    t2.validFrom < t.validTo
             );
  

Вы также можете сделать это с кумулятивным максимальным:

 select t.*
from (select t.*,
             max(validTo) over (partition by id, name 
                                order by validFrom
                                rows between unbounded preceding and 1 preceding
                               ) as prev_validTo
      from t
     ) t
where prev_validTo >= validFrom;
  

Комментарии:

1. OP может включать значение равно для сравнения дат ( >= вместо > ), если перекрытия периодов должны учитывать граничные дни.

2. @EzLo . . . Это хороший момент. Основываясь на образце данных, я не думаю, что границы должны быть одинаковыми.

3. Я совершенно уверен, что если вы не включите сравнение столбцов значений (т. Е. t.value <> t2.value ), ваши запросы будут возвращать каждую строку.

4. @GordonLinoff третий пример работает для меня как шарм. К сожалению, первый пример возвращает все записи (также отдельные строки). Также второй пример, но причина distinct в том, что любая комбинация id-name возвращается только один раз.

5. @JM_ . . . Это было верно для первых двух (которые были исправлены), но не для третьего.