При работе с системами контроля доступа (СКД) часто возникает задача выявления сотрудников, у которых количество событий «Вход» не совпадает с количеством событий «Выход». Это может быть связано с тем, что сотрудник прошел через «черный ход», не приложил карту или возник технический сбой контроллера. Рассмотрим способы решения этой задачи с помощью механизмов запросов 1С.
Если нам нужно просто найти идентификаторы (ID) сотрудников, у которых общее число записей за период не является четным (что косвенно указывает на отсутствие пары для какого-то события), можно воспользоваться математическим подходом в запросе. Разберем пример, где мы делим количество записей на 2 и сравниваем результат с целым числом.
ВЫБРАТЬ
Турникет.ID КАК СотрудникID,
КОЛИЧЕСТВО(Турникет.ТипСобытия) КАК ВсегоСобытий
ИЗ
Справочник.Турникет КАК Турникет
ГДЕ
Турникет.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
СГРУППИРОВАТЬ ПО
Турникет.ID
ИМЕЮЩИЕ
(ВЫРАЗИТЬ(КОЛИЧЕСТВО(Турникет.ТипСобытия) / 2 КАК ЧИСЛО(15, 0))) <> (ВЫРАЗИТЬ(КОЛИЧЕСТВО(Турникет.ТипСобытия) / 2 КАК ЧИСЛО(15, 2)))
В данном примере мы используем секцию ИМЕЮЩИЕ для фильтрации результатов группировки. Если результат деления количества записей на 2 с округлением до целого не равен результату без округления (с точностью до 2 знаков), значит, число записей нечетное.
Более точный метод — это присвоение каждому типу события числового веса. Например, событию «Вход» присвоим значение 1, а событию «Выход» — значение -1. Если сумма этих значений за период равна нулю, значит, все входы закрыты выходами. Если сумма отлична от нуля, у сотрудника есть «повисшее» событие.
Проанализируем ситуацию на примере следующего запроса:
ВЫБРАТЬ
Турникет.Сотрудник КАК Сотрудник,
СУММА(ВЫБОР
КОГДА Турникет.Событие = ЗНАЧЕНИЕ(Перечисление.ТипыСобытийСКД.Вход)
ТОГДА 1
ИНАЧЕ -1
КОНЕЦ) КАК Баланс
ИЗ
Справочник.Турникет КАК Турникет
ГДЕ
Турникет.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
СГРУППИРОВАТЬ ПО
Турникет.Сотрудник
ИМЕЮЩИЕ
СУММА(ВЫБОР
КОГДА Турникет.Событие = ЗНАЧЕНИЕ(Перечисление.ТипыСобытийСКД.Вход)
ТОГДА 1
ИНАЧЕ -1
КОНЕЦ) <> 0
Важный момент: этот способ наглядно показывает «сальдо» посещений. Положительное число означает, что сотрудник вошел, но не вышел. Отрицательное — что зафиксирован выход без входа.
При реализации подобных отчетов на больших объемах данных (десятки и сотни тысяч записей) необходимо учитывать несколько критических факторов:
Сотрудник и Дата установлен признак «Индексировать». В противном случае запрос будет выполнять полное сканирование таблицы (Full Scan), что приведет к длительному ожиданию и блокировкам базы.Дата (содержащее и дату, и время). Если же поля разделены, в запросе их придется объединять программно, что делает невозможным использование индексов.Если данные СКД находятся во внешней базе данных, мы можем применить специфические функции SQL для сопоставления строк. Выясним причину расхождений, используя оконные функции, которые позволяют заглянуть в «следующую» запись того же сотрудника:
SELECT
EmployeeID,
EventTime,
EventType
FROM (
SELECT
ID AS EmployeeID,
Date + Time AS EventTime,
Type AS EventType,
LEAD(Type) OVER (PARTITION BY ID ORDER BY Date, Time) AS NextEventType
FROM TurnstileTable
WHERE Date BETWEEN '2016-12-01' AND '2016-12-01'
) AS Sub
WHERE EventType = NextEventType -- Находит два входа или два выхода подряд
Использование функции LEAD позволяет сравнить текущее событие со следующим. Если тип события повторяется (например, «Вход» и следом снова «Вход»), система мгновенно идентифицирует нарушение последовательности.