В практике разработки на платформе 1С часто возникают задачи аналитического характера, когда необходимо не просто получить список выполненных заданий, но и вычислить специфические метрики эффективности. Одной из таких задач является поиск «точки экватора» — момента времени, в который была выполнена ровно половина запланированной на день работы. Это позволяет понять, в какой половине дня сотрудники наиболее продуктивны.
В данной статье мы разберем, как решить эту задачу исключительно средствами языка запросов, не прибегая к построчной обработке результатов в цикле на встроенном языке. Мы проанализируем несколько подходов: от классического метода, работающего на старых версиях платформы, до современного способа с использованием новых функций 8.3.20+.
Представим, что у нас есть два регистра. Первый содержит задания на день, второй — фактические записи о выполнении этих заданий с точностью до секунды. Нам необходимо для каждого дня найти время завершения задачи, чей порядковый номер соответствует 50% от общего количества выполненных задач за этот день.
Для решения нам потребуется выполнить следующие шаги:
Этот метод является универсальным и работает на любой версии платформы 8.x. Его суть заключается в соединении таблицы самой с собой по условию Время >= Время. Таким образом, для каждой записи количество соответствующих ей строк из присоединенной таблицы и будет ее порядковым номером.
Рассмотрим пример реализации такого запроса:
// Шаг 1: Подготовка исходных данных
ВЫБРАТЬ
Выполнение.Период КАК День,
Выполнение.Задание КАК Задание,
Выполнение.Период КАК ВремяВыполнения
ПОМЕСТИТЬ ВТ_ТаблицаВыполнения
ИЗ
РегистрСведений.РезультатыРабот КАК Выполнение
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Нумерация записей внутри дня
ВЫБРАТЬ
Т1.День,
Т1.Задание,
Т1.ВремяВыполнения,
КОЛИЧЕСТВО(Т2.ВремяВыполнения) КАК ПорядковыйНомер
ПОМЕСТИТЬ ВТ_ТаблицаПронумерованная
ИЗ
ВТ_ТаблицаВыполнения КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТаблицаВыполнения КАК Т2
ПО Т1.День = Т2.День
И Т1.ВремяВыполнения >= Т2.ВремяВыполнения
СГРУППИРОВАТЬ ПО
Т1.День,
Т1.Задание,
Т1.ВремяВыполнения
;
Важный нюанс: Если две задачи были выполнены в одну и ту же секунду, условие >= может отработать некорректно, присвоив им одинаковые или «перепрыгивающие» номера. Чтобы избежать этого, в реальных задачах рекомендуется добавлять в условие сравнения уникальный идентификатор записи (например, Ссылка или НомерСтроки).
Если ваша конфигурация работает на платформе 8.3.20 и выше, использование функции АВТОНОМЕРЗАПИСИ() будет значительно более эффективным решением. Классическое соединение имеет квадратичную сложность и на больших объемах данных (тысячи записей в день) может привести к существенным задержкам. Функция АВТОНОМЕРЗАПИСИ() работает линейно.
Проанализируем пример использования этой функции:
ВЫБРАТЬ ПЕРВЫЕ 1000000 // Необходимо для работы УПОРЯДОЧИТЬ в ВТ
ВТ_Данные.День КАК День,
ВТ_Данные.Задание КАК Задание,
ВТ_Данные.Время КАК Время,
АВТОНОМЕРЗАПИСИ() КАК НомерПП
ПОМЕСТИТЬ ВТ_ДанныеСНумерацией
ИЗ
ВТ_Данные КАК ВТ_Данные
УПОРЯДОЧИТЬ ПО
ВТ_Данные.День,
ВТ_Данные.Время
;
Обратите внимание, что для корректной работы нумерации во временной таблице обязательно наличие секции УПОРЯДОЧИТЬ ПО. Без нее порядок присвоения номеров будет произвольным, что сделает результат бессмысленным.
После того как мы пронумеровали записи, нам нужно выяснить, какой именно номер является «половиной». Здесь важно определиться с правилами округления. Если заданий 5, то половина — это 3-е задание. Если заданий 6, то по строгим правилам статистики медиана — это среднее между 3 и 4 заданием, но в рамках 1С обычно достаточно взять 3-е или 4-е (округление вверх или вниз).
Рассмотрим, как вычислить целевой номер и получить результат:
////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Определение целевого номера (50% объема)
ВЫБРАТЬ
Пронумерованные.День,
ВЫРАЗИТЬ((КОЛИЧЕСТВО(Пронумерованные.НомерПП) + 1) / 2 КАК ЧИСЛО(10, 0)) КАК НомерМедианы
ПОМЕСТИТЬ ВТ_ЦелевойНомер
ИЗ
ВТ_ДанныеСНумерацией КАК Пронумерованные
СГРУППИРОВАТЬ ПО
Пронумерованные.День
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Получение времени «экватора»
ВЫБРАТЬ
Цели.День,
Данные.Время КАК ВремяВыполненияПоловины,
Данные.Задание КАК ПоследнееЗаданиеВПоловине
ИЗ
ВТ_ЦелевойНомер КАК Цели
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДанныеСНумерацией КАК Данные
ПО Цели.День = Данные.День
И Цели.НомерМедианы = Данные.НомерПП
Проанализируем ситуацию, когда задания имеют разный вес. Если одна починка компьютера занимает 10 минут, а другая — 4 часа, то поиск по количеству записей (50% штук) не даст объективного времени выполнения 50% трудозатрат. В этом случае мы рекомендуем:
Длительность.Также стоит помнить о производительности. Любые временные таблицы в запросах должны быть проиндексированы по полям, участвующим в соединениях. В нашем случае это поле День и поле с порядковым номером. Для этого после создания временной таблицы ВТ_ДанныеСНумерацией следует добавить инструкцию ИНДЕКСИРОВАТЬ ПО.
Использование системы компоновки данных (СКД) может еще сильнее упростить задачу. В СКД предусмотрены вычисляемые поля и агрегатные функции, такие как ПроцентВГруппе. Если отчет строится в СКД, можно вывести детальные записи и просто наложить отбор на системное поле процента, что избавит от необходимости писать сложные вложенные запросы с нумерацией.