2017-10-19 141 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