Расчет последовательных отсутствий в SQL - PullRequest
1 голос
/ 13 октября 2009

Мне нужно рассчитать всех сотрудников, у которых есть X последовательных пропусков в диапазоне дат в SQL.

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

tblAbsences
EmployeeID int
AbsenceDate datetime

tblCalendar
WorkDay datetime

У кого-нибудь есть идеи, как рассчитать последовательные пропуски? Пример: все сотрудники, у которых есть 3 последовательных отсутствия в период с 01.01.2009 по 03.01.2009.

Ответы [ 2 ]

1 голос
/ 16 октября 2009

Проверено в 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
;
1 голос
/ 13 октября 2009

Это должно работать для вас. GROUP BY на ConsecDates, чтобы найти, кто отсутствовал более X раз.

select a.*, 
        (
            select min(b.absenceDate) from tblAbsences b where a.employeeId = b.employeeId 
            and b.absenceDate >= a.absenceDate
            and not exists ( 
                select 1 from tblabsences c where c.employeeId = b.employeeId and dateadd( dd, 1, b.absenceDate) = c.absenceDate  
            )
) ConsecDates
from dbo.tblAbsences a
order by a.AbsenceDate asc
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...