Principalmente, ho due tipi di intervalli di tempo:
presence time e absence time
absence time può essere di diversi tipi (es. pause, assenze, giorno speciale e così via) e gli intervalli di tempo possono sovrapporsi e / o intersecarsi.
È non sicuro, che esistono solo combinazioni plausibili di intervalli di dati grezzi, ad es. gli intervalli di presenza sovrapposti non hanno senso, ma possono esistere. Ho cercato di identificare gli intervalli di tempo-presenza risultanti in molti modi ora - per me, il più comodo sembra essere quello che segue.
;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";
vedi SQL-Fiddle per alcuni dati dimostrativi.
I dati grezzi esistono in diverse tabelle sotto forma di "starttime" - "endtime"o "starttime" - "duration".
L'idea era di ottenere un elenco ordinato di tutti i timestamp con una somma mobile "bitmasked" di intervalli aperti ogni volta per stimare il tempo di presenza.
Il violino funziona e fornisce risultati stimati, anche se gli inizi di intervalli diversi sono uguali. In questo esempio non vengono utilizzati indici.
È questo il modo giusto per raggiungere un compito controverso o esiste un modo più elegante per farlo?
Se pertinente per la risposta: la quantità di dati sarà fino a diverse diecimila serie di dati per dipendente per tabella. sql-2012 non è disponibile per calcolare una somma variabile di predecessori in linea in forma aggregata.
modificare:
Ho appena eseguito la query con una quantità maggiore di dati di test (1000, 10.000, 100.000, 1 milione) e vedo che il tempo di esecuzione aumenta in modo esponenziale. Ovviamente una bandiera di avvertimento, giusto?
Ho modificato la query e rimosso l'aggregazione della somma variabile tramite un aggiornamento stravagante.
Ho aggiunto una tabella ausiliaria:
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 mi sono trasferito calcolando la somma variabile in questo luogo:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
Il tempo di esecuzione è diminuito a 3 secondi rispetto a 1 milione di voci nella tabella "orario di lavoro".
La domanda rimane la stessa : qual è il modo più efficace per risolverlo?
[this]. Mi piace solo meglio delle doppie virgolette, immagino.