Vis*_*hal 5 t-sql sql-server-2008
我有
TABLE EMPLOYEE - ID,DATE,IsPresent
Run Code Online (Sandbox Code Playgroud)
我想计算一个员工存在的最长连线.现在的位将是假的,他没有来的几天.所以我想计算他连续几天来到办公室的最长天数.我有Date列字段是独一无二......所以我试过这种方式 -
Select Id,Count(*) from Employee where IsPresent=1
Run Code Online (Sandbox Code Playgroud)
但上面没有用......有人能指导我如何计算连线吗?....我相信人们已经遇到过这个...我试过在网上搜索但是......并没有理解它...... .请帮帮我..
编辑这是查询的 SQL Server 版本:
with LowerBound as (select second_day.EmployeeId
, second_day."DATE" as LowerDate
, row_number() over (partition by second_day.EmployeeId
order by second_day."DATE") as RN
from T second_day
left outer join T first_day
on first_day.EmployeeId = second_day.EmployeeId
and first_day."DATE" = dateadd(day, -1, second_day."DATE")
and first_day.IsPresent = 1
where first_day.EmployeeId is null
and second_day.IsPresent = 1)
, UpperBound as (select first_day.EmployeeId
, first_day."DATE" as UpperDate
, row_number() over (partition by first_day.EmployeeId
order by first_day."DATE") as RN
from T first_day
left outer join T second_day
on first_day.EmployeeId = second_day.EmployeeId
and first_day."DATE" = dateadd(day, -1, second_day."DATE")
and second_day.IsPresent = 1
where second_day.EmployeeId is null
and first_day.IsPresent = 1)
select LB.EmployeeID, max(datediff(day, LowerDate, UpperDate) + 1) as LongestStreak
from LowerBound LB
inner join UpperBound UB
on LB.EmployeeId = UB.EmployeeId
and LB.RN = UB.RN
group by LB.EmployeeId
Run Code Online (Sandbox Code Playgroud)
SQL Server版本的测试数据:
create table T (EmployeeId int
, "DATE" date not null
, IsPresent bit not null
, constraint T_PK primary key (EmployeeId, "DATE")
)
insert into T values (1, '2000-01-01', 1);
insert into T values (2, '2000-01-01', 0);
insert into T values (3, '2000-01-01', 0);
insert into T values (3, '2000-01-02', 1);
insert into T values (3, '2000-01-03', 1);
insert into T values (3, '2000-01-04', 0);
insert into T values (3, '2000-01-05', 1);
insert into T values (3, '2000-01-06', 1);
insert into T values (3, '2000-01-07', 0);
insert into T values (4, '2000-01-01', 0);
insert into T values (4, '2000-01-02', 1);
insert into T values (4, '2000-01-03', 1);
insert into T values (4, '2000-01-04', 1);
insert into T values (4, '2000-01-05', 1);
insert into T values (4, '2000-01-06', 1);
insert into T values (4, '2000-01-07', 0);
insert into T values (5, '2000-01-01', 0);
insert into T values (5, '2000-01-02', 1);
insert into T values (5, '2000-01-03', 0);
insert into T values (5, '2000-01-04', 1);
insert into T values (5, '2000-01-05', 1);
insert into T values (5, '2000-01-06', 1);
insert into T values (5, '2000-01-07', 0);
Run Code Online (Sandbox Code Playgroud)
抱歉,这是用 Oracle 编写的,因此请替换为适当的 SQL Server 日期算法。
假设:
(EmployeeId, Date)not null如果该员工缺少日期,则表明他们不在场。(用于处理数据系列的开始和结束,但也意味着中间缺少日期将中断条纹。根据要求,可能会出现问题。
with LowerBound as (select second_day.EmployeeId
, second_day."DATE" as LowerDate
, row_number() over (partition by second_day.EmployeeId
order by second_day."DATE") as RN
from T second_day
left outer join T first_day
on first_day.EmployeeId = second_day.EmployeeId
and first_day."DATE" = second_day."DATE" - 1
and first_day.IsPresent = 1
where first_day.EmployeeId is null
and second_day.IsPresent = 1)
, UpperBound as (select first_day.EmployeeId
, first_day."DATE" as UpperDate
, row_number() over (partition by first_day.EmployeeId
order by first_day."DATE") as RN
from T first_day
left outer join T second_day
on first_day.EmployeeId = second_day.EmployeeId
and first_day."DATE" = second_day."DATE" - 1
and second_day.IsPresent = 1
where second_day.EmployeeId is null
and first_day.IsPresent = 1)
select LB.EmployeeID, max(UpperDate - LowerDate + 1) as LongestStreak
from LowerBound LB
inner join UpperBound UB
on LB.EmployeeId = UB.EmployeeId
and LB.RN = UB.RN
group by LB.EmployeeId
Run Code Online (Sandbox Code Playgroud)测试数据:
create table T (EmployeeId number(38)
, "DATE" date not null check ("DATE" = trunc("DATE"))
, IsPresent number not null check (IsPresent in (0, 1))
, constraint T_PK primary key (EmployeeId, "DATE")
)
/
insert into T values (1, to_date('2000-01-01', 'YYYY-MM-DD'), 1);
insert into T values (2, to_date('2000-01-01', 'YYYY-MM-DD'), 0);
insert into T values (3, to_date('2000-01-01', 'YYYY-MM-DD'), 0);
insert into T values (3, to_date('2000-01-02', 'YYYY-MM-DD'), 1);
insert into T values (3, to_date('2000-01-03', 'YYYY-MM-DD'), 1);
insert into T values (3, to_date('2000-01-04', 'YYYY-MM-DD'), 0);
insert into T values (3, to_date('2000-01-05', 'YYYY-MM-DD'), 1);
insert into T values (3, to_date('2000-01-06', 'YYYY-MM-DD'), 1);
insert into T values (3, to_date('2000-01-07', 'YYYY-MM-DD'), 0);
insert into T values (4, to_date('2000-01-01', 'YYYY-MM-DD'), 0);
insert into T values (4, to_date('2000-01-02', 'YYYY-MM-DD'), 1);
insert into T values (4, to_date('2000-01-03', 'YYYY-MM-DD'), 1);
insert into T values (4, to_date('2000-01-04', 'YYYY-MM-DD'), 1);
insert into T values (4, to_date('2000-01-05', 'YYYY-MM-DD'), 1);
insert into T values (4, to_date('2000-01-06', 'YYYY-MM-DD'), 1);
insert into T values (4, to_date('2000-01-07', 'YYYY-MM-DD'), 0);
insert into T values (5, to_date('2000-01-01', 'YYYY-MM-DD'), 0);
insert into T values (5, to_date('2000-01-02', 'YYYY-MM-DD'), 1);
insert into T values (5, to_date('2000-01-03', 'YYYY-MM-DD'), 0);
insert into T values (5, to_date('2000-01-04', 'YYYY-MM-DD'), 1);
insert into T values (5, to_date('2000-01-05', 'YYYY-MM-DD'), 1);
insert into T values (5, to_date('2000-01-06', 'YYYY-MM-DD'), 1);
insert into T values (5, to_date('2000-01-07', 'YYYY-MM-DD'), 0);
Run Code Online (Sandbox Code Playgroud)
缺少 groupby。
选择整个办公室的总人日(每个人)出勤率。
Select Id,Count(*) from Employee where IsPresent=1
Run Code Online (Sandbox Code Playgroud)
选择每个员工的人日出勤率。
Select Id,Count(*)
from Employee
where IsPresent=1
group by id;
Run Code Online (Sandbox Code Playgroud)
但这仍然不好,因为它计算的是出勤总天数,而不是连续出勤时间。
您需要做的是使用另一个日期列 date2 构建一个临时表。date2 设置为今天。该表是员工缺勤的所有天数的列表。
create tmpdb.absentdates as
Select id, date, today as date2
from EMPLOYEE
where IsPresent=0
order by id, date;
Run Code Online (Sandbox Code Playgroud)
因此,诀窍是计算两个缺席日之间的日期差,以找到连续存在日的长度。现在,在 date2 中填写每个员工的下一个缺勤日期。每个员工的最新记录不会更新,而是保留今天的值,因为数据库中没有比今天日期更晚的记录。
update tmpdb.absentdates
set date2 =
select min(a2.date)
from
tmpdb.absentdates a1,
tmpdb.absentdates a2
where a1.id = a2.id
and a1.date < a2.date
Run Code Online (Sandbox Code Playgroud)
上面的查询通过对自身执行联接来更新自身,并且可能会导致查询死锁,因此最好创建临时表的两个副本。
create tmpdb.absentdatesX as
Select id, date
from EMPLOYEE
where IsPresent=0
order by id, date;
create tmpdb.absentdates as
select *, today as date2
from tmpdb.absentdatesX;
Run Code Online (Sandbox Code Playgroud)
您需要插入雇用日期,假设数据库中每个员工的最早日期就是雇用日期。
insert into tmpdb.absentdates a
select a.id, min(e.date), today
from EMPLOYEE e
where a.id = e.id
Run Code Online (Sandbox Code Playgroud)
现在用下一个稍后缺席的日期更新 date2 以便能够执行 date2 - date。
update tmpdb.absentdates
set date2 =
select min(x.date)
from
tmpdb.absentdates a,
tmpdb.absentdatesX x
where a.id = x.id
and a.date < x.date
Run Code Online (Sandbox Code Playgroud)
这将列出雇员连续存在的天数:
select id, datediff(date2, date) as continuousPresence
from tmpdb.absentdates
group by id, continuousPresence
order by id, continuousPresence
Run Code Online (Sandbox Code Playgroud)
但你只想最长的连续记录:
select id, max(datediff(date2, date) as continuousPresence)
from tmpdb.absentdates
group by id
order by id
Run Code Online (Sandbox Code Playgroud)
但是,上面仍然存在问题,因为 datediff 没有考虑节假日和周末。
所以我们依靠记录数作为合法工作日。
create tmpdb.absentCount as
Select a.id, a.date, a.date2, count(*) as continuousPresence
from EMPLOYEE e, tmpdb.absentdates a
where e.id = a.id
and e.date >= a.date
and e.date < a.date2
group by a.id, a.date
order by a.id, a.date;
Run Code Online (Sandbox Code Playgroud)
请记住,每次使用像 count 这样的聚合器时,您都需要按所选项目列表进行分组,因为必须按它们进行聚合是常识。
现在选择最大连胜
select id, max(continuousPresence)
from tmpdb.absentCount
group by id
Run Code Online (Sandbox Code Playgroud)
列出连胜的日期:
select id, date, date2, continuousPresence
from tmpdb.absentCount
group by id
having continuousPresence = max(continuousPresence);
Run Code Online (Sandbox Code Playgroud)
上面可能有一些错误(sql server tsql),但这是总体思路。
| 归档时间: |
|
| 查看次数: |
2459 次 |
| 最近记录: |