Em geral, tenho dois tipos de intervalos de tempo:
presence time
e absence time
absence time
podem ser de tipos diferentes (por exemplo, intervalos, ausências, dias especiais etc.) e os intervalos de tempo podem se sobrepor e / ou se cruzar.
É não , com certeza, que apenas as combinações plausíveis de intervalos de existir em dados brutos, por exemplo. intervalos de presença sobrepostos não fazem sentido, mas podem existir. Tentei identificar os intervalos de tempo de presença resultantes de várias maneiras agora - para mim, o mais confortável parece ser o seguinte.
;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";
veja SQL-Fiddle para alguns dados de demonstração.
Os dados brutos existem em tabelas diferentes na forma de "starttime" - "endtime"
ou "starttime" - "duration"
.
A idéia era obter uma lista ordenada de todos os registros de data e hora com uma soma rolante "mascarada" de intervalos abertos a cada momento para estimar o tempo de presença.
O violino funciona e fornece resultados estimados, mesmo que os tempos de início de intervalos diferentes sejam iguais. Nenhum índice é usado neste exemplo.
É este o caminho certo para realizar tarefas questionadas ou existe uma maneira mais elegante para isso?
Se relevante para responder: a quantidade de dados será de até dez mil conjuntos de dados por funcionário por tabela. O sql-2012 não está disponível para calcular uma soma contínua de predecessores in-line de forma agregada.
editar:
Apenas executou a consulta contra uma quantidade maior de dados de teste (1000, 10.000, 100.000, 1 milhão) e pode ver que o tempo de execução aumenta exponencialmente. Obviamente, uma bandeira de aviso, certo?
Alterei a consulta e removi a agregação da soma rolante por uma atualização peculiar.
Eu adicionei uma tabela 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" )
e mudei o cálculo da soma rolante para este local:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
O tempo de execução diminuiu para 3 segundos em relação a 1 milhão de entradas na tabela "horário de trabalho".
A pergunta permanece a mesma : qual é a maneira mais eficaz de resolver isso?
[this]
. Eu gosto mais do que aspas duplas, eu acho.Respostas:
Não consigo responder à sua pergunta da melhor maneira possível. Mas posso oferecer uma maneira diferente de resolver o problema, que pode ou não ser melhor. Ele tem um plano de execução razoavelmente plano e acho que terá um bom desempenho. (Estou ansioso para saber, compartilhe os resultados!)
Peço desculpas por usar meu próprio estilo de sintaxe em vez do seu - isso ajuda a consultar a assistente quando tudo se alinha no seu lugar habitual.
A consulta está disponível em um SqlFiddle . Fiz uma sobreposição para o EmpID 1 apenas para ter certeza de que isso estava coberto. Se, eventualmente, você descobrir que as sobreposições não podem ocorrer nos dados de presença, poderá remover a consulta final e os
Dense_Rank
cálculos.Nota: o desempenho desta consulta seria aprimorado. Você combinou as três tabelas e adicionou uma coluna para indicar que tipo de tempo era: trabalho, interrupção ou ausência.
E por que todos os CTEs, você pergunta? Porque cada um é forçado pelo que eu preciso fazer com os dados. Há um agregado, ou eu preciso colocar uma condição WHERE em uma função de janela ou usá-la em uma cláusula em que funções de janela não são permitidas.
Agora vou sair e ver se não consigo pensar em outra estratégia para conseguir isso. :)
Para diversão, incluo aqui um "diagrama" que fiz para ajudar a resolver o problema:
Os três conjuntos de traços (separados por espaços) representam, em ordem: dados de presença, dados de ausência e o resultado desejado.
fonte