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.Respuestas:
No puedo responder a tu pregunta sobre la mejor manera. Pero puedo ofrecer una forma diferente de resolver el problema, que puede o no ser mejor. Tiene un plan de ejecución razonablemente plano, y creo que funcionará bien. (Estoy ansioso por saberlo, ¡así que comparte los resultados!)
Pido disculpas por usar mi propio estilo de sintaxis en lugar del tuyo. Me ayuda a consultar la magia cuando todo se alinea en su lugar habitual.
La consulta está disponible en un SqlFiddle . Lancé una superposición para EmpID 1 solo para asegurarme de que tenía eso cubierto. Si finalmente encuentra que las superposiciones no pueden ocurrir en los datos de presencia, puede eliminar la consulta final y los
Dense_Rank
cálculos.Nota: el rendimiento de esta consulta mejoraría si combinara las tres tablas y agregara una columna para indicar qué tipo de tiempo fue: trabajo, descanso o ausencia.
¿Y por qué todos los CTE, preguntas? Porque cada uno está obligado por lo que necesito hacer con los datos. Hay un agregado, o necesito poner una condición WHERE en una función de ventana o usarla en una cláusula donde las funciones de ventana no están permitidas.
Ahora me voy a ir y ver si no puedo pensar en otra estrategia para lograr esto. :)
Por diversión incluyo aquí un "diagrama" que hice para ayudar a resolver el problema:
Los tres conjuntos de guiones (separados por espacios) representan, en orden: datos de presencia, datos de ausencia y el resultado deseado.
fuente