Перекрытия относительно просты (но вам может потребоваться беспокоиться о временах '<=' vs '<' и '> =' vs '>'):
SELECT x1.*, x2.*, "Overlap"
FROM tblRentalRates AS x1, tblRentalRates AS x2
WHERE x1.rate_startdate < x2.rate_enddate
AND x1.rate_enddate > x2.rate_startdate
AND x1.rate_id < x2.date_id -- Avoid repeats with x1, x2 interchanged
Определить полный охват значительно сложнее. Этому не помогает причудливость каждой СУБД, имеющей собственную (несовместимую) систему функций обработки даты или представления даты.
Я собираюсь начать с обозначения СУБД, с которым я больше всего знаком (IBM Informix Dynamic Server), и затем попытаюсь перевести его на MS SQL Server.
Нам нужно установить набор строк, которые перекрывают целевой год (строки, которые заканчиваются 1 января или позднее и начинаются 31 декабря или раньше):
SELECT * FROM tblRentalRates AS rr
WHERE rr.rate_enddate >= MDY( 1, 1,YEAR(TODAY))
AND rr.rate_startdate <= MDY(12,31,YEAR(TODAY))
Далее нам нужно определить гранулярность значений даты. Название DATETIME предполагает, что значения могут хранить как компонент времени, так и компонент даты, но было бы намного проще, если бы мы могли предположить, что гранулярность составляет «1 день». В качестве альтернативы нам нужно знать, включена ли дата окончания в диапазон или нет - это диапазон открытый-закрытый или открытый-открытый (или закрытый-открытый, или закрытый-закрытый).
Теперь нам нужно найти пары записей в вышеприведенном списке, для которых конечная дата более ранней является более чем на один день раньше начальной даты более поздней и для которой нет строки между ними - они образуют пробелы в охват. Я предполагаю, что открыто-открытое представление с гранулярностью 1 день (поэтому строка с началом '2009-06-01' и концом '2009-06-01' представляет собой информацию за один день, а строка с начало / конец '2009-06-02' / '2009-06-03' содержит информацию за два дня и т. д., и между этими двумя строками не может быть никакой даты, не перекрывая нас).
SELECT r1.*, r2.*, "Gap"
FROM (SELECT * FROM tblRentalRates AS rr
WHERE rr.rate_enddate >= MDY( 1, 1,YEAR(TODAY))
AND rr.rate_startdate <= MDY(12,31,YEAR(TODAY))
) AS r1
CROSS JOIN
(SELECT * FROM tblRentalRates AS rr
WHERE rr.rate_enddate >= MDY( 1, 1,YEAR(TODAY))
AND rr.rate_startdate <= MDY(12,31,YEAR(TODAY))
) AS r2
WHERE r1.rate_enddate < r2.rate_startdate - INTERVAL(1) DAY TO DAY
AND NOT EXISTS
(SELECT * FROM tblRentalRates AS r3
WHERE r3.rate_enddate > r1.rate_enddate)
AND r3.rate_startdate < r2.rate_startdate)
)
Учитывая данные этого примера:
INSERT INTO tblRentalRates VALUES(1, '2008-12-19', '2009-01-03', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(2, '2009-01-09', '2009-01-13', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(3, '2009-02-19', '2009-02-23', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(4, '2009-02-24', '2009-02-28', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(5, '2009-03-01', '2009-03-23', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(6, '2009-03-29', '2009-11-03', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(7, '2009-11-29', '2009-12-13', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(8, '2009-12-15', '2009-12-28', 1.23, 2.23);
INSERT INTO tblRentalRates VALUES(9, '2009-12-29', '2010-01-03', 1.23, 2.23);
Запрос дает:
1 2008-12-19 2009-01-03 1.23 2.23 2 2009-01-09 2009-01-13 1.23 2.23 Gap
2 2009-01-09 2009-01-13 1.23 2.23 3 2009-02-19 2009-02-23 1.23 2.23 Gap
5 2009-03-01 2009-03-23 1.23 2.23 6 2009-03-29 2009-11-03 1.23 2.23 Gap
6 2009-03-29 2009-11-03 1.23 2.23 7 2009-11-29 2009-12-13 1.23 2.23 Gap
7 2009-11-29 2009-12-13 1.23 2.23 8 2009-12-15 2009-12-28 1.23 2.23 Gap
Очевидно, это все еще не нотация MS SQL Server. Однако я собираюсь постулировать, что те, кто знаком с обозначениями, используемыми в MS SQL Server, могут адаптировать материал, описанный выше, чтобы он работал - так же как упражнение для читателя .