2009-10-12 14 views
1

J'ai besoin de calculer tous les employés qui ont X nombre d'absences consécutives dans une plage de dates en SQL.Calcul des absences consécutives dans SQL

Nous avons une table d'absences avec 1 enregistrement pour chaque jour d'absence d'un employé et une table de calendrier avec les jours de travail pour l'année.

tblAbsences 
EmployeeID int 
AbsenceDate datetime 

tblCalendar 
WorkDay datetime 

Est-ce que quelqu'un a des idées pour calculer des absences consécutives? Exemple: Tous les employés qui ont 3 absences consécutives entre le 1/1/2009 et le 3/1/2009.

+0

question de Jeff sur l'insigne Woot pourrait être pertinente ici: http://stackoverflow.com/questions/1176011/sql-to-determine-minimum-sequential-days-of-access –

Répondre

1

Cela devrait fonctionner pour vous. GROUP BY sur ConsecDates pour trouver qui était absent plus de X nombre de fois.

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 
+0

Cette fonctionne très bien, sauf que j'ai également besoin d'exclure les week-ends. Exemple: jeudi, vendredi et lundi de congé est de 3 jours consécutifs. (Samedi et dimanche ne seraient pas dans tblAbsences) –

+0

J'ai ajouté 3 jours si le b.AbsenceDate est vendredi, et il semble fonctionner. Merci! ... ET (DATEADD (dd, 1, b.absenceDate) = c.absenceDate OR ( (DATENAME (dw, b.absenceDate) = 'vendredi') ET DATEADD (dd, 3, b.absenceDate) = c.absenceDate) –

+0

En ce qui concerne les vacances? Cette solution n'utilise pas du tout votre table de calendrier –

1

Testé dans PostgreSQL; SQL utilise des exemples de valeurs de publication.

Aucune clé primaire n'est définie dans les tables fournies et le code ci-dessous en tient compte. Mieux serait d'ajouter des clés primaires et d'optimiser le code ci-dessous pour les exploiter: une meilleure qualité des données, une meilleure performance, un code plus propre, des gens plus heureux.

La suppression du préfixe tbl offre plus de flexibilité dans l'implémentation de la base de données. La table, la vue et le synonyme peuvent être utilisés de manière interchangeable sans impact sur le code qui fait référence à l'objet de base de données ou à la convention de dénomination.

/* 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 
; 

Profitez. Version Stripped ci-dessous:

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 
; 
Questions connexes