我需要计算在SQL的日期范围内有X个连续缺席的所有员工。
我们有一个缺勤表,每天缺少一名员工记录1个记录,以及一个包含当年工作日的日历表。
tblAbsences
EmployeeID int
AbsenceDate datetime
tblCalendar
WorkDay datetime
有没有人有任何想法如何计算连续缺席?示例:在2009年1月1日至2009年3月1日期间连续3次缺席的所有员工。
答案 0 :(得分:1)
这对你有用。在ConsecDates上的GROUP BY找到谁缺席超过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
答案 1 :(得分:1)
在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
;