В основному, у мене є два види інтервалів часу:
presence time
і absence time
absence time
можуть бути різних типів (наприклад, перерви, відсутність, особливий день тощо), а інтервали часу можуть перетинатися та / або перетинатися.
Це НЕ обов'язково, що тільки правдоподібні комбінації інтервалів існують у вихідних даних, наприклад. перекриття інтервалів присутності не має сенсу, але вони можуть існувати. Зараз я намагався багатьма способами визначити виникаючі інтервали часу присутності - для мене найбільш комфортним здається наступний.
;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";
див. SQL-Fiddle для деяких демо-даних.
Необхідні дані існують у різних таблицях у вигляді "starttime" - "endtime"
або "starttime" - "duration"
.
Ідея полягала в тому, щоб отримати упорядкований перелік кожної часової позначки з "розробленою" сумою відкритих інтервалів кожного разу, щоб оцінити час присутності.
Загадка працює і дає оцінені результати, навіть якщо початки різних інтервалів рівні. У цьому прикладі не використовуються індекси.
Це правильний спосіб досягти сумнівного завдання чи є більш елегантний спосіб для цього?
Якщо відповідне для відповіді: кількість даних становитиме до декількох десяти тисяч наборів даних на одного працівника на таблицю. sql-2012 не доступний для обчислення сумарної суми попередників, вбудованих у сукупність.
редагувати:
Щойно виконаний запит проти більшої кількості тестових даних (1000, 10 000, 100 000, 1 мільйон) і видно, що час виконання збільшується в експоненціальному масштабі. Очевидно попереджувальний прапор, правда?
Я змінив запит і вилучив сукупність поточної суми за допомогою химерного оновлення.
Я додав допоміжну таблицю:
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" )
і я перемістив підрахунок поточної суми до цього місця:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
Час виконання скоротився до 3 секунд щодо 1 мільйона записів у таблиці "робочий час".
Питання залишається тим самим : який найефективніший спосіб вирішити це?
[this]
. Мені це подобається краще, ніж подвійні цитати, напевно.