Hauptsächlich habe ich zwei Arten von Zeitintervallen:
presence time
und absence time
absence time
Es kann sich um verschiedene Arten handeln (z. B. Pausen, Abwesenheiten, besondere Tage usw.), und Zeitintervalle können sich überschneiden und / oder überschneiden.
Es ist nicht sicher, dass in Rohdaten nur plausible Kombinationen von Intervallen existieren, z. Überlappende Anwesenheitsintervalle sind nicht sinnvoll, können aber existieren. Ich habe jetzt auf viele Arten versucht, die resultierenden Anwesenheitszeitintervalle zu identifizieren - für mich scheint das bequemste das folgende zu sein.
;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";
Einige Demo-Daten finden Sie unter SQL-Fiddle .
Die Rohdaten liegen in verschiedenen Tabellen in Form von "starttime" - "endtime"
oder vor "starttime" - "duration"
.
Die Idee war, eine geordnete Liste jedes Zeitstempels mit einer "bitmaskierten" fortlaufenden Summe offener Intervalle zu jedem Zeitpunkt zu erhalten, um die Anwesenheitszeit abzuschätzen.
Die Geige funktioniert und liefert geschätzte Ergebnisse, auch wenn die Startzeiten in unterschiedlichen Intervallen gleich sind. In diesem Beispiel werden keine Indizes verwendet.
Ist dies der richtige Weg, um eine fragliche Aufgabe zu erfüllen, oder gibt es dafür einen eleganteren Weg?
Falls für die Beantwortung relevant: Die Datenmenge beträgt bis zu mehreren zehntausend Datensätze pro Mitarbeiter und Tabelle. sql-2012 ist nicht verfügbar, um eine fortlaufende Summe der Vorgänger inline insgesamt zu berechnen.
bearbeiten:
Führen Sie die Abfrage gerade für eine größere Anzahl von Testdaten (1000, 10.000, 100.000, 1 Million) aus und sehen Sie, dass die Laufzeit exponentiell zunimmt. Offensichtlich eine Warnflagge, oder?
Ich habe die Abfrage geändert und die Aggregation der fortlaufenden Summe durch ein eigenartiges Update entfernt.
Ich habe eine Hilfstabelle hinzugefügt:
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" )
und ich habe die Berechnung der rollierenden Summe an diesen Ort verschoben:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
Die Laufzeit verringerte sich auf 3 Sekunden in Bezug auf 1 Million Einträge in der "Arbeitszeit" -Tabelle.
Die Frage bleibt gleich : Was ist der effektivste Weg, um dies zu lösen?
[this]
. Ich mag das einfach besser als doppelte Anführungszeichen, denke ich.