Проверено в PostgreSQL; SQL использует примеры значений из публикации.
В предоставленных таблицах не определены первичные ключи, и приведенный ниже код решает эту проблему. Лучше было бы добавить первичные ключи и оптимизировать приведенный ниже код, чтобы использовать их: лучшее качество данных, лучшую производительность, более чистый код, более счастливых людей.
Удаление префикса tbl обеспечивает большую гибкость в реализации базы данных. Таблицу, представление и синоним можно использовать взаимозаменяемо, не влияя на код, который ссылается на объект базы данных, или нарушая соглашение об именах.
/* period length as the desired number of consecutive days */
/* time window as the period to be analyzed */
SELECT DISTINCT
/* Consolidate employees with multiple periods */
a.employeeid
FROM
(SELECT
/* Generate all possible periods */
pk_c.workday begin_date,
/* End date for given period length; less one for closed boundaries */
LEAD(pk_c.workday,3-1,NULL) OVER (ORDER BY pk_c.workday) end_date
FROM (SELECT DISTINCT
/* No calendar PK, remove dupes; if PK, pull in-line view up */
c.workday
FROM sandbox.calendar c) pk_c
) p
INNER JOIN sandbox.absences a ON
/* Match absences with periods */
(a.absencedate BETWEEN p.begin_date AND p.end_date)
WHERE
/* In time window desired; exclude periods extending beyond boundaries */
(p.begin_date BETWEEN '2009-01-01' AND '2009-03-01'
AND /* NOT NULL exclusion implied for periods beyond calendar boundaries */
p.end_date BETWEEN '2009-01-01' AND '2009-03-01')
GROUP BY
a.employeeid,
/* Also group period, display only employee */
p.begin_date
HAVING
/* Number of absence days to match to the period length */
/* DISTINCT due to missing absences PK; if PK, drop DISTINCT */
COUNT(DISTINCT a.absencedate) = 3
;
Наслаждайтесь. Раздетая версия ниже:
SELECT DISTINCT
a.employeeid
FROM
(SELECT
pk_c.workday begin_date,
LEAD(pk_c.workday,3-1,NULL) OVER (ORDER BY pk_c.workday) end_date
FROM (SELECT DISTINCT c.workday FROM sandbox.calendar c) pk_c) p
INNER JOIN sandbox.absences a ON
(a.absencedate BETWEEN p.begin_date AND p.end_date)
WHERE
(p.begin_date BETWEEN '2009-01-01' AND '2009-03-01'
AND p.end_date BETWEEN '2009-01-01' AND '2009-03-01')
GROUP BY
a.employeeid, p.begin_date
HAVING
COUNT(DISTINCT a.absencedate) = 3
;