2017-10-19 24 views
1

Vous êtes si doué pour le code. Je peux utiliser un meilleur SQL qui me dira le nombre de jours dans un mois que chaque employé a travaillé. Chaque employé peut entrer et sortir plusieurs fois par jour et travailler jusqu'à minuit. Si elles travaillent plus de minuit, ça compte comme 2 jours de travail. Si elles travaillaient après minuit et arrivaient plus tard le même jour et partaient avant minuit, ce temps aurait déjà été compté puisque c'était le même jour.Comptez le nombre de jours travaillés

Cela fonctionne mais est-il un moyen plus facile?

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 

Répondre

1

Essayez ceci, pas nécessaire CTEs:

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 

j'ai changé DATEPART(Day, TimeIn/Out)-CONVERT(DATE, TimeIn/Out) pour gérer différents mois/années. Ce n'est peut-être pas nécessaire en fonction de ce que vous avez dit, mais la performance sera à peu près la même que DATEPART() de toute façon.

Fondamentalement, l'approche ici est d'obtenir une liste de jours distincts par employeeID, indépendamment du fait que le jour provient de la colonne TimeIn ou . UNION fonctionne parfaitement pour cela, car il peut combiner les deux colonnes, et il supprime les résultats en double du jeu de résultats, ce qui nous laisse avec une liste distincte de jours/employeeID. Ensuite, il suffit de compter les lignes par employéID.

+1

Si un employé donne un coup de poing à 23h00 le 15, travaille 26 heures (bénisse son coeur) et puis sort à 1 heure du matin le 17, le 16 ci-dessus manquera. – Brian

+0

@Brian C'est vraiment marrant, merci pour le rire. Cet employé devrait reconsidérer sa ligne de travail. Mais sérieusement, je ne cherchais qu'à optimiser le code d'OP qui ne tiendrait pas non plus compte de cette situation. Je pourrais écrire quelque chose qui en rendrait compte si nécessaire, mais à moins que cette situation ne soit un souci, cela ne ferait que rendre le code beaucoup plus complexe qu'il ne devrait l'être. –

+0

De rien :). Quand j'ai lu le code de l'OP pour la première fois, je pensais qu'il avait expliqué ce cas, mais après examen, je vois que vous avez raison - il ne l'a pas fait. Le code que vous avez présenté semble certainement correspondre à l'exemple de code de l'OP. – Brian