Dans l'ensemble, j'ai deux types d'intervalles de temps:
presence time
et absence time
absence time
peuvent être de différents types (par exemple, pauses, absences, jour spécial, etc.) et les intervalles de temps peuvent se chevaucher et / ou se recouper.
Il n'est pas certain que seules des combinaisons plausibles d'intervalles existent dans les données brutes, par exemple. les intervalles de présence qui se chevauchent n'ont pas de sens, mais peuvent exister. J'ai essayé d'identifier les intervalles de temps de présence résultants de plusieurs façons maintenant - pour moi, le plus confortable semble être celui qui suit.
;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";
voir SQL-Fiddle pour quelques données de démonstration.
Les données brutes existent dans différentes tables sous forme de "starttime" - "endtime"
ou "starttime" - "duration"
.
L'idée était d'obtenir une liste ordonnée de chaque horodatage avec une somme continue "bitmaskée" d'intervalles ouverts à chaque fois pour estimer le temps de présence.
Le violon fonctionne et donne des résultats estimés, même si les heures de début de différents intervalles sont égales. Aucun indice n'est utilisé dans cet exemple.
Est-ce la bonne façon de réaliser la tâche remise en question ou existe-t-il une manière plus élégante pour cela?
Si cela est pertinent pour répondre: la quantité de données peut atteindre plusieurs dizaines de milliers de jeux de données par employé et par table. sql-2012 n'est pas disponible pour calculer une somme cumulée de prédécesseurs en ligne dans l'ensemble.
Éditer:
Vient d'exécuter la requête sur une plus grande quantité de données de test (1000, 10 000, 100 000, 1 million) et peut voir que le temps d'exécution augmente de façon exponentielle. De toute évidence, un drapeau d'avertissement, non?
J'ai changé la requête et supprimé l'agrégation de la somme mobile par une mise à jour excentrique.
J'ai ajouté une table auxiliaire:
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" )
et j'ai déplacé le calcul de la somme mobile à cet endroit:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
Le temps d'exécution a diminué à 3 secondes pour 1 million d'entrées dans la table "worktime".
La question reste la même : quel est le moyen le plus efficace de résoudre ce problème?
[this]
. J'aime mieux que les guillemets doubles, je suppose.