В основном у меня есть два вида временных интервалов:
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]
. Думаю, мне это нравится лучше, чем двойные кавычки.Ответы:
Я не могу ответить на ваш вопрос относительно абсолютно лучшего способа. Но я могу предложить другой способ решения проблемы, который может быть или не быть лучше. У него достаточно плоский план выполнения, и я думаю, что он будет работать хорошо. (Я хочу знать, так что поделитесь результатами!)
Я прошу прощения за использование моего собственного стиля синтаксиса вместо вашего - он помогает магии запросов приходить ко мне, когда все выстраивается в привычное место.
Запрос доступен в SqlFiddle . Я добавил перекрытие для EmpID 1 только для того, чтобы быть уверенным, что это покрыто. Если в конечном итоге вы обнаружите, что в данных присутствия не может быть перекрытий, вы можете удалить окончательный запрос и
Dense_Rank
вычисления.Примечание: производительность этого запроса будет улучшена, если вы объедините три таблицы и добавите столбец, чтобы указать, сколько времени это было: работа, перерыв или отсутствие.
И почему все CTE, спросите вы? Потому что каждый вынужден делать то, что мне нужно сделать с данными. Есть агрегат, или мне нужно поместить условие WHERE в оконную функцию или использовать его в предложении, где оконные функции не разрешены.
Теперь я собираюсь уйти и посмотреть, не могу ли я придумать другую стратегию для достижения этой цели. :)
Для развлечения я привожу здесь «диаграмму», которую я сделал, чтобы помочь решить проблему:
Три набора штрихов (разделенных пробелами) представляют по порядку: данные о присутствии, данные об отсутствии и желаемый результат.
источник