En general, tengo dos tipos de intervalos de tiempo:
presence time y absence time
absence time pueden ser de diferentes tipos (por ejemplo, descansos, ausencias, días especiales, etc.) y los intervalos de tiempo pueden superponerse y / o cruzarse.
Es no seguro, que sólo existen combinaciones posibles de intervalos en los datos en bruto, por ejemplo. los intervalos de presencia superpuestos no tienen sentido, pero pueden existir. He tratado de identificar los intervalos de tiempo de presencia resultantes de muchas maneras ahora; para mí, el más cómodo parece ser el siguiente.
;with "timestamps"
as
(
select
"id" = row_number() over ( order by "empId", "timestamp", "opening", "type" )
, "empId"
, "timestamp"
, "type"
, "opening"
from
(
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 1 as "type" from "worktime" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
union all
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 2 as "type" from "break" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
union all
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 3 as "type" from "absence" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
) as data
)
select
T1."empId"
, "starttime" = T1."timestamp"
, "endtime" = T2."timestamp"
from
"timestamps" as T1
left join "timestamps" as T2
on T2."empId" = T1."empId"
and T2."id" = T1."id" + 1
left join "timestamps" as RS
on RS."empId" = T2."empId"
and RS."id" <= T1."id"
group by
T1."empId", T1."timestamp", T2."timestamp"
having
(sum( power( 2, RS."type" ) * RS."opening" ) = 2)
order by
T1."empId", T1."timestamp";
ver SQL-Fiddle para algunos datos de demostración.
Los datos en bruto existen en diferentes tablas en forma de "starttime" - "endtime"o "starttime" - "duration".
La idea era obtener una lista ordenada de cada marca de tiempo con una suma continua "enmascarada" de intervalos abiertos en cada momento para estimar el tiempo de presencia.
El violín funciona y proporciona resultados estimados, incluso si los tiempos de inicio de diferentes intervalos son iguales. No se utilizan índices en este ejemplo.
¿Es esta la forma correcta de lograr una tarea cuestionada o hay una forma más elegante para esto?
Si es relevante para responder: la cantidad de datos será de hasta varios diez mil conjuntos de datos por empleado por tabla. sql-2012 no está disponible para calcular una suma continua de predecesores en línea en conjunto.
editar:
Acabo de ejecutar la consulta contra una mayor cantidad de datos de prueba (1000, 10,000, 100,000, 1 millón) y puedo ver que el tiempo de ejecución aumenta exponencialmente. Obviamente una bandera de advertencia, ¿verdad?
Cambié la consulta y eliminé la agregación de la suma acumulada por una actualización peculiar.
He agregado una tabla auxiliar:
create table timestamps
(
"id" int
, "empId" int
, "timestamp" datetime
, "type" int
, "opening" int
, "rolSum" int
)
create nonclustered index "idx" on "timestamps" ( "rolSum" ) include ( "id", "empId", "timestamp" )
y me mudé calculando la suma acumulada a este lugar:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
El tiempo de ejecución disminuyó a 3 segundos con respecto a 1 millón de entradas en la tabla "tiempo de trabajo".
La pregunta sigue siendo la misma : ¿Cuál es la forma más efectiva de resolver esto?
[this]. Supongo que me gusta más que las comillas dobles.