2012-01-19 15 views
6

Estoy tratando de ejecutar algunos informes y tener que lidiar con todo el problema de horas de trabajo de los empleados que cruzan la medianoche. Sin embargo, se me ocurre que podría dividir los registros que cruzan la medianoche en dos registros, como si el empleado marcara la salida a medianoche y simultáneamente volviera a la medianoche, evitando así el problema de la medianoche.Registros de tiempo dividido a través de la medianoche

Así que si tengo:

EmployeeId InTime      OutTime 
---   ----------------------- ----------------------- 
1   2012-01-18 19:50:04.437 2012-01-19 03:30:02.433 

¿Qué crees que sería la forma más elegante para dividir este registro, así:

EmployeeId InTime      OutTime 
---   ----------------------- ----------------------- 
1   2012-01-18 19:50:04.437 2012-01-19 00:00:00.000 
1   2012-01-19 00:00:00.000 2012-01-19 03:30:02.433 

Y sí, he pensado a fondo a través de lo efectos esto podría tener en la funcionalidad existente ... razón por la cual estoy optando por hacer esto en una tabla temporal que no afectará la funcionalidad existente.

+1

Salida http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=42516&bcsi_scan_0070CC03028EB70D=JJykjVHNGapjNgIKjEMdLQkAAADDDLIH&bcsi_scan_filename=topic.asp no es exactamente similar. Más sobre su solución basada en SQL 2k. Pero espero que pueda darte algunos consejos. – vmvadivel

+0

Así que su cruce de la medianoche ... ¿cuál es el problema ... el total de horas trabajadas en un turno determinado? o si cruza ambas fechas, ¿en qué fecha debe aparecer? – DRapp

+0

@DRapp Para los fines de "este" informe (aunque no todos) el trabajo debe aparecer en la fecha en que realmente pertenece ... no necesariamente la fecha en que el empleado comenzó su turno. Es por eso que creo que esta es la mejor solución para lograrlo de una manera que los datos tengan más sentido para trabajar. –

Respuesta

6

Esto podría ayudar:

DECLARE @tbl TABLE 
    (
     EmployeeId INT, 
     InTime DATETIME, 
     OutTime DATETIME 
    ) 

INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (1,'2012-01-18 19:50:04.437','2012-01-19 03:30:02.433') 
INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (2,'2012-01-18 19:50:04.437','2012-01-18 20:30:02.433') 
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (3,'2012-01-18 16:15:00.000','2012-01-19 00:00:00.000') 
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (4,'2012-01-18 00:00:00.000','2012-01-18 08:15:00.000') 
SELECT 
    tbl.EmployeeId, 
    tbl.InTime, 
    DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0) AS OutTime 
FROM 
    @tbl AS tbl 
WHERE 
    DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1 
UNION ALL 
SELECT 
    tbl.EmployeeId, 
    CASE WHEN DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1 
     THEN DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0) 
     ELSE tbl.InTime 
    END AS InTime, 
    tbl.OutTime 
FROM @tbl AS tbl 
ORDER BY EmployeeId 
0

Pruebe esto, porque puede hacer una inserción de una selección y dentro de su selección puede establecer los valores para usar para ser días diferentes.

para añadir la nueva fila:

insert into table ("EMPLOYEE_ID","INTIME","OUTTIME") values 
SELECT EMPLOYEE_ID,date(INTIME),OUTTIME 
FROM table 
where date(intime) < date(outtime) 

Actualización de la fila original:

update table 
set outtime =date(outtime) 
where date(intime)= date(outtime) 
1

Si por el informe, a continuación, sólo debe estar capaz de hacer una consulta/unión que dé dos registros durante esas condiciones desde el original que comienza ... Sin tener SQL-Se rver 2008, solo puedo ofrecerte una consulta de pseudo-código.

La primera parte del obtiene todos los registros basados ​​en cualquiera que sea su condición de rango para mostrar. El valor de "OutTime" es condicional ... si está en el mismo día, luego no se cruzan, simplemente use el tiempo de salida. Si ES el día siguiente, use casting para construir dinámicamente una fecha 'AAAA-MM-DD' (que por defecto será 00:00:00) como desee como hora OUT.

La UNIÓN SÓLO tomará los mismos registros calificados en PRIMERO, donde las fechas de entrada/salida son DIFERENTES. Por lo tanto, SABEMOS que queremos que el Tiempo Fuera sea como el Tiempo de Entrada, pero basado en el tiempo "00:00:00", por lo que se realiza exactamente el mismo lanzamiento de un campo de fecha/hora, y para estos registros, solo use el valor final "OutTime" tal como está.

La columna extra para "TimeSplit" de "1" o "2" es para asegurarnos de que todavía podemos agrupar por ID de empleado, pero a partir de eso, asegúrese de que las entradas "1" (turno inicial) son las primeras, seguido de cualquiera para la misma persona respectiva, tiene una entrada de '2' para la superposición del día en su turno.

select 
     tc.EmployeeID, 
     '1' as TimeSplit, 
     tc.InTime, 
     case when datepart(dd, tc.InTime) = datepart(dd, tc.OutTime) 
     then tc.OutTime 
     else CAST(CAST(datepart(yyyy, tc.OutTime) AS varchar) 
       +'-'+ CAST(datepart(mm, tc.OutTime) AS varchar) 
       +'-'+ CAST(datepart(dd, tc.OutTime) AS varchar) AS DATETIME) 
     end as OutTime 
    from 
     TimeCard tc 
    where 
     YourDateRangeConditions... 
    ORDER BY 
     tc.EmployeeID, 
     TimeSplit 
UNION ALL 
select 
     tc.EmployeeID, 
     '2' as TimeSplit, 
     CAST( CAST(datepart(yyyy, tc.OutTime) AS varchar) 
     +'-'+ CAST(datepart(mm, tc.OutTime) AS varchar) 
     +'-'+ CAST(datepart(dd, tc.OutTime) AS varchar) AS DATETIME) 
     end as InTime 
     tc.OutTime 
    from 
     TimeCard tc 
    where 
     YourDateRangeConditions... 
     AND NOT datepart(dd, tc.InTime) = datepart(dd, tc.OutTime) 
2

La siguiente solución utiliza una tabla de números (en la forma de un subconjunto de la tabla master..spt_values sistema) para dividir los intervalos de tiempo. Puede dividir rangos que abarcan un número arbitrario de días (hasta 2048 con spt_values, pero con su propia tabla de números puede establecer un máximo diferente). Los casos específicos de los rangos que abarcan de 1 y 2 días no se abordan aquí, pero creo que el método es lo suficientemente ligero para que pueda probar:

; 
WITH LaborHours (EmployeeId, InTime, OutTime) AS (
    SELECT 
    1, 
    CAST('2012-01-18 19:50:04.437' AS datetime), 
    CAST('2012-01-18 03:30:02.433' AS datetime) 
), 
HoursSplit AS (
    SELECT 
    h.*, 
    SubInTime = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 0, 0), 
    SubOutTime = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 1, 0) 
    FROM LaborHours h 
    INNER JOIN master..spt_values v 
     ON number BETWEEN 0 AND DATEDIFF(DAY, h.InTime, h.OutTime) 
    WHERE v.type = 'P' 
), 
HoursSubstituted AS (
    SELECT 
    EmployeeId, 
    InTime = CASE WHEN InTime > SubInTime THEN InTime ELSE SubInTime END, 
    OutTime = CASE WHEN OutTime < SubOutTime THEN OutTime ELSE SubOutTime END 
    FROM HoursSplit 
) 
SELECT * 
FROM HoursSubstituted 

Básicamente, se trata de un método de dos pasos.

Primero usamos la tabla de números para duplicar cada fila tantas veces como el número de días que abarca el rango y para preparar subintervalos 'estándar' comenzando a la medianoche y terminando a la siguiente medianoche.

A continuación, comparamos el comienzo de un subintervalo con el comienzo del rango para ver si es el primer subintervalo, en cuyo caso usamos InTime como su comienzo. De forma similar, comparamos las terminaciones para ver si debemos usar OutTime o solo la medianoche como el final de ese subrango.

Cuestiones relacionadas