При разработке отчетов в 1С программисты часто сталкиваются с необходимостью вычислить время, прошедшее между двумя событиями, которые зафиксированы в регистре сведений. Например, сколько времени прошло с момента создания заказа до его выполнения. Сложность заключается в том, что каждое событие — это отдельная строка в регистре, а стандартная функция РАЗНОСТЬДАТ предназначена для работы со значениями в рамках одной строки. Разберем подробно, как решить эту задачу с помощью языка запросов.
Рассмотрим типичную ошибку начинающего разработчика. Пытаясь вычислить разницу, программист пишет конструкцию следующего вида:
РАЗНОСТЬДАТ(
ВЫБОР КОГДА СтатусЗаказа = ЗНАЧЕНИЕ(Перечисление.Статусы.Выполнен) ТОГДА Период КОНЕЦ,
ВЫБОР КОГДА СтатусЗаказа = ЗНАЧЕНИЕ(Перечисление.Статусы.Создан) ТОГДА Период КОНЕЦ,
СЕКУНДА
)
Проанализируем ситуацию: для системы 1С каждая запись в регистре сведений уникальна. В одной конкретной строке статус не может быть одновременно и «Создан», и «Выполнен». Следовательно, в одной строке первое выражение ВЫБОР вернет дату, а второе — NULL (или пустую дату), и наоборот. Функция РАЗНОСТЬДАТ не может корректно обработать NULL, поэтому результатом становится пустое значение или ошибка.
Чтобы вычислить разницу, нам нужно «схлопнуть» две разные строки (событие А и событие Б) в одну. Для этого мы используем механизм группировки по ключевому полю (например, по Заказу) и агрегатную функцию МАКСИМУМ.
Разберем по шагам создание правильного запроса:
ВЫБОР.Пример реализации через временную таблицу:
// Шаг 1: Получаем таблицу, где даты начала и конца стоят в одной строке
ВЫБРАТЬ
ИсторияСтатусов.Заказ КАК Заказ,
МАКСИМУМ(ВЫБОР
КОГДА ИсторияСтатусов.СтатусЗаказа = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Создан)
ТОГДА ИсторияСтатусов.Период
ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
КОНЕЦ) КАК ДатаСоздания,
МАКСИМУМ(ВЫБОР
КОГДА ИсторияСтатусов.СтатусЗаказа = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Выполнен)
ТОГДА ИсторияСтатусов.Период
ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
КОНЕЦ) КАК ДатаВыполнения
ПОМЕСТИТЬ ВТ_ДатыЗаказов
ИЗ
РегистрСведений.ИсторияСтатусовЗаказов КАК ИсторияСтатусов
СГРУППИРОВАТЬ ПО
ИсторияСтатусов.Заказ;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Вычисляем разницу между полученными датами
ВЫБРАТЬ
ВТ_ДатыЗаказов.Заказ,
ВТ_ДатыЗаказов.ДатаСоздания,
ВТ_ДатыЗаказов.ДатаВыполнения,
РАЗНОСТЬДАТ(ВТ_ДатыЗаказов.ДатаСоздания, ВТ_ДатыЗаказов.ДатаВыполнения, СЕКУНДА) КАК ДлительностьВСекундах
ИЗ
ВТ_ДатыЗаказов КАК ВТ_ДатыЗаказов
ГДЕ
ВТ_ДатыЗаказов.ДатаСоздания > ДАТАВРЕМЯ(1, 1, 1)
И ВТ_ДатыЗаказов.ДатаВыполнения > ДАТАВРЕМЯ(1, 1, 1)
Важный нюанс: Обратите внимание на использование ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1). Если этого не сделать, функция МАКСИМУМ может вернуть NULL, что помешает дальнейшим расчетам. В финальной выборке мы добавили условие ГДЕ, чтобы отсечь заказы, которые еще не прошли оба статуса.
Функция РАЗНОСТЬДАТ возвращает целое число. Если мы получили разницу в секундах (например, 3665 секунд), пользователю будет неудобно это читать. Нам нужно привести это к виду «1 час 1 минута 5 секунд».
Рассмотрим, как вычислить компоненты времени прямо в запросе (хотя это делает его громоздким):
ВЫРАЗИТЬ(РазностьВСекундах / 3600 - 0.5 КАК ЧИСЛО(10, 0))ВЫРАЗИТЬ((РазностьВСекундах - Часы * 3600) / 60 - 0.5 КАК ЧИСЛО(10, 0))Однако более элегантным решением в 1С является использование Вычисляемых полей СКД. Вместо сложных математических операций в теле запроса, мы можем использовать функции общего модуля или встроенные функции языка выражений СКД. Например:
ЦелоеЧисло(РазностьВСекундах / 3600) (для получения часов в СКД).
Если ваша конфигурация работает на современной платформе, задачу «получить время следующего события» можно решить гораздо проще. Рассмотрим использование функции ВЫЧИСЛИТЬ_ВЫРАЖЕНИЕ.
В языке запросов для СКД можно использовать оператор СЛЕДУЮЩИЙ, чтобы получить период следующей записи для того же заказа, не прибегая к сложным группировкам. Это позволяет рассчитать время пребывания в каждом статусе, а не только между двумя конкретными.
Проанализируем еще несколько важных аспектов, которые помогут сделать ваш отчет точным:
ГрафикиРаботы.ИсторияСтатусов содержит миллионы записей, группировка МАКСИМУМ(ВЫБОР...) может работать медленно. В этом случае рассмотрите возможность записи «Длительности нахождения в статусе» прямо в регистр при смене статуса программно. Тогда отчет превратится в простое суммирование ресурса.Подведем итог: для вычисления разницы между периодами разных строк регистра сведений мы используем группировку по объекту и агрегацию дат через МАКСИМУМ. Это позволяет собрать нужные данные в одну строку и применить к ним стандартную функцию РАЗНОСТЬДАТ.