2017-10-19 12 views
1

皆さんはコードをすっきりと作成することができます。私は各従業員が働いた月の日数を教えてくれるより良いSQLを使うことができます。各従業員は1日に何度もパンチイン/アウトしたり、深夜に働くことができます。彼らが真夜中に働くならば、それは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

一切のCTEは必要ありません、これを試してみてください:

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 

私は別ヶ月/年処理するためにCONVERT(DATE, TimeIn/Out)DATEPART(Day, TimeIn/Out)を変更しました。あなたが言ったことに基づいて必要ではないかもしれませんが、パフォーマンスはとにかくDATEPART()と同じになります。

基本的にここでのアプローチは、TimeInまたはTimeOut列からの日かどうかにかかわらず、employeeIDごとに異なる日のリストを取得することです。 UNIONは2つの列を組み合わせることができ、結果セットから重複する結果を削除するため、days/employeeIDという明確なリストが残ります。次に、employeeIDあたりの行数を数えるだけです。

+1

従業員が午後11時にパンチインする場合。 15日には26時間働いて(彼の心を祝福して)、17日に午前1時にパンチすると、上記は16番を欠場する。 – Brian

+0

@Brianそれは本当に面白いです、笑いをありがとう。その従業員は自分の仕事のラインを再考すべきです。しかし、真剣に、私はその状況を説明しないOPのコードを最適化することを目指していました。必要に応じてそれを説明できるものを書くことができますが、そのような状況が関心事でなければ、必要以上に複雑なコードにしかならないでしょう。 –

+0

あなたは大歓迎です:)。 OPのコードを最初に読んだとき、私は彼がこのケースを説明したと思っていましたが、再検討の結果、あなたは正しいと思っていました。あなたが提示したコードは、OPのサンプルコードと機能的に一致しているようです。 – Brian