В основном у меня есть два вида временных интервалов:
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]
. Думаю, мне это нравится лучше, чем двойные кавычки.