2017-10-19 140 views
1

你们是如此善于打理代码。我可以使用更好的SQL来告诉我每个员工每月工作的天数。每个员工每天可以进出几次,并且可以在午夜之前工作。如果他们在午夜工作,则计为工作2天。如果他们在午夜时分工作并在当天晚些时候进入,并在下一个午夜之前离开,那么这段时间自同一天以来就已经被计算在内。计算工作天数

这有效,但有没有更简单的方法?

IF OBJECT_ID ('dbo.ZTable1', 'U') IS NOT NULL 
    DROP TABLE dbo.ZTable1; 
GO 
CREATE TABLE dbo.ZTable1 ([EmployeeId] Numeric (5,0), [TimeIn] datetime, 
[TimeOut] datetime) 

INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 1,'2017-09- 
13 12:19','2017-09-14 00:01' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 1,'2017-09- 
14 12:15','2017-09-15 00:01' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 1,'2017-09- 
15 12:35','2017-09-16 00:01' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 1,'2017-09- 
16 07:56','2017-09-16 10:31' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 1,'2017-09- 
16 11:56','2017-09-16 16:31' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 2,'2017-09- 
13 15:26','2017-09-14 00:00' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 2,'2017-09- 
14 15:29','2017-09-15 00:00' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 2,'2017-09- 
15 15:27','2017-09-16 00:01' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 3,'2017-09- 
13 15:25','2017-09-14 00:01' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 3,'2017-09- 
14 15:25','2017-09-15 00:00' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 3,'2017-09- 
15 15:26','2017-09-16 00:00' 
INSERT INTO dbo.ZTable1 ([EmployeeId],[TimeIn],[TimeOut]) SELECT 3,'2017-09- 
16 06:55','2017-09-16 15:27' 

GO; 

With Step1 as ( --< Build temp table of in punch days 
Select [EmployeeId], DATEPART (DAY ,[TimeIn]) as WorkDay 
from dbo.ZTable1 
), 
Step2 as ( --< Build temp table of out punch days 
Select [EmployeeId], DATEPART (DAY ,[TimeOut]) as WorkDay 
from dbo.ZTable1 
), 
Step3 as ( --< merges both in and put punch tables 
Select 
Case when s1.[EmployeeId] is NULL then s2.[EmployeeId] else s1.[EmployeeId] end as Employee, 
case when s1.WorkDay is NULL then s2.WorkDay else s1.WorkDay end as WorkDate 
from Step1 s1 
full outer join Step2 s2 on s1.[EmployeeId] = s2.[EmployeeId] and s1.WorkDay = s2.WorkDay 
), 
Step4 as ( --< Organizes temp table 
Select Distinct Employee, WorkDate 
from Step3 
group by Employee, WorkDate 
) 
Select Employee, Count (Employee) as NumDays 
from Step4 
Where Employee > 0 
Group by Employee 
Order by Employee 


DROP TABLE dbo.ZTable1 

Output (Result) 
Employee NumDays 
1   4 
2   4 
3   4 

回答

1

试试这个,没有热膨胀系数需要:

SELECT EmployeeID, COUNT(1) as NumDays 
FROM (
    SELECT EmployeeID, CONVERT(DATE,TimeIn) DistinctDays 
    FROM ZTable1 

    UNION 

    SELECT EmployeeID, CONVERT(DATE,TimeOut) 
    FROM ZTable1 
    ) A 
GROUP BY EmployeeID 

我改变DATEPART(Day, TimeIn/Out)CONVERT(DATE, TimeIn/Out)处理不同月/年。根据你的说法,这可能不是必要的,但是性能总体上与DATEPART()大致相同。

基本上,此处的方法是获取每个employeeID不同天数的列表,无论该日期是来自TimeIn还是TimeOut列。 UNION完美适用于此,因为它可以组合两列,并从结果集中删除重复结果,这为结果集留下了明确的days/employeeID列表。然后,这只是计算每个员工ID的行数。

+1

如果员工在下午11点打卡, 15日工作26小时(保佑他的心),然后在17日凌晨1点打出,上述将错过16日。 – Brian

+0

@Brian这真的很有趣,谢谢你的笑声。该员工应该重新考虑他的工作。但严重的是,我只是旨在优化OP的代码,这也不能解释这种情况。如果有必要,我可以写一些能够解释它的东西,但除非这种情况是一个问题,否则它只会使代码比需要的复杂得多。 –

+0

不客气:)。当我第一次阅读OP的代码时,我认为他已经解释了这个案例,但在审查时我发现你是对的 - 他没有。您提供的代码在功能上似乎与OP的示例代码相匹配。 – Brian