Как в запросе 1С определить время выполнения половины объема работ за день

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С часто возникают задачи аналитического характера, когда необходимо не просто получить список выполненных заданий, но и вычислить специфические метрики эффективности. Одной из таких задач является поиск «точки экватора» — момента времени, в который была выполнена ровно половина запланированной на день работы. Это позволяет понять, в какой половине дня сотрудники наиболее продуктивны.

В данной статье мы разберем, как решить эту задачу исключительно средствами языка запросов, не прибегая к построчной обработке результатов в цикле на встроенном языке. Мы проанализируем несколько подходов: от классического метода, работающего на старых версиях платформы, до современного способа с использованием новых функций 8.3.20+.

Постановка задачи и логика решения

Представим, что у нас есть два регистра. Первый содержит задания на день, второй — фактические записи о выполнении этих заданий с точностью до секунды. Нам необходимо для каждого дня найти время завершения задачи, чей порядковый номер соответствует 50% от общего количества выполненных задач за этот день.

Для решения нам потребуется выполнить следующие шаги:

  1. Подготовить таблицу выполненных работ с привязкой к конкретной дате.
  2. Пронумеровать выполненные работы внутри каждого дня в хронологическом порядке.
  3. Рассчитать общее количество работ за каждый день и определить целевой «средний» номер.
  4. Соединить таблицу с номерами и таблицу с целевым индексом, чтобы получить точное время.

Способ 1: Классическая нумерация строк через левое соединение

Этот метод является универсальным и работает на любой версии платформы 8.x. Его суть заключается в соединении таблицы самой с собой по условию Время >= Время. Таким образом, для каждой записи количество соответствующих ей строк из присоединенной таблицы и будет ее порядковым номером.

Рассмотрим пример реализации такого запроса:


// Шаг 1: Подготовка исходных данных
ВЫБРАТЬ
    Выполнение.Период КАК День,
    Выполнение.Задание КАК Задание,
    Выполнение.Период КАК ВремяВыполнения
ПОМЕСТИТЬ ВТ_ТаблицаВыполнения
ИЗ
    РегистрСведений.РезультатыРабот КАК Выполнение
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Нумерация записей внутри дня
ВЫБРАТЬ
    Т1.День,
    Т1.Задание,
    Т1.ВремяВыполнения,
    КОЛИЧЕСТВО(Т2.ВремяВыполнения) КАК ПорядковыйНомер
ПОМЕСТИТЬ ВТ_ТаблицаПронумерованная
ИЗ
    ВТ_ТаблицаВыполнения КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТаблицаВыполнения КАК Т2
        ПО Т1.День = Т2.День
            И Т1.ВремяВыполнения >= Т2.ВремяВыполнения
СГРУППИРОВАТЬ ПО
    Т1.День,
    Т1.Задание,
    Т1.ВремяВыполнения
;

Важный нюанс: Если две задачи были выполнены в одну и ту же секунду, условие >= может отработать некорректно, присвоив им одинаковые или «перепрыгивающие» номера. Чтобы избежать этого, в реальных задачах рекомендуется добавлять в условие сравнения уникальный идентификатор записи (например, Ссылка или НомерСтроки).

Способ 2: Современный подход с функцией АВТОНОМЕРЗАПИСИ

Если ваша конфигурация работает на платформе 8.3.20 и выше, использование функции АВТОНОМЕРЗАПИСИ() будет значительно более эффективным решением. Классическое соединение имеет квадратичную сложность и на больших объемах данных (тысячи записей в день) может привести к существенным задержкам. Функция АВТОНОМЕРЗАПИСИ() работает линейно.

Проанализируем пример использования этой функции:


ВЫБРАТЬ ПЕРВЫЕ 1000000 // Необходимо для работы УПОРЯДОЧИТЬ в ВТ
    ВТ_Данные.День КАК День,
    ВТ_Данные.Задание КАК Задание,
    ВТ_Данные.Время КАК Время,
    АВТОНОМЕРЗАПИСИ() КАК НомерПП
ПОМЕСТИТЬ ВТ_ДанныеСНумерацией
ИЗ
    ВТ_Данные КАК ВТ_Данные
УПОРЯДОЧИТЬ ПО
    ВТ_Данные.День,
    ВТ_Данные.Время
;

Обратите внимание, что для корректной работы нумерации во временной таблице обязательно наличие секции УПОРЯДОЧИТЬ ПО. Без нее порядок присвоения номеров будет произвольным, что сделает результат бессмысленным.

Расчет медианного номера и получение итогового времени

После того как мы пронумеровали записи, нам нужно выяснить, какой именно номер является «половиной». Здесь важно определиться с правилами округления. Если заданий 5, то половина — это 3-е задание. Если заданий 6, то по строгим правилам статистики медиана — это среднее между 3 и 4 заданием, но в рамках 1С обычно достаточно взять 3-е или 4-е (округление вверх или вниз).

Рассмотрим, как вычислить целевой номер и получить результат:


////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Определение целевого номера (50% объема)
ВЫБРАТЬ
    Пронумерованные.День,
    ВЫРАЗИТЬ((КОЛИЧЕСТВО(Пронумерованные.НомерПП) + 1) / 2 КАК ЧИСЛО(10, 0)) КАК НомерМедианы
ПОМЕСТИТЬ ВТ_ЦелевойНомер
ИЗ
    ВТ_ДанныеСНумерацией КАК Пронумерованные
СГРУППИРОВАТЬ ПО
    Пронумерованные.День
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Получение времени «экватора»
ВЫБРАТЬ
    Цели.День,
    Данные.Время КАК ВремяВыполненияПоловины,
    Данные.Задание КАК ПоследнееЗаданиеВПоловине
ИЗ
    ВТ_ЦелевойНомер КАК Цели
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДанныеСНумерацией КАК Данные
        ПО Цели.День = Данные.День
            И Цели.НомерМедианы = Данные.НомерПП

Рекомендации по оптимизации и точности

Проанализируем ситуацию, когда задания имеют разный вес. Если одна починка компьютера занимает 10 минут, а другая — 4 часа, то поиск по количеству записей (50% штук) не даст объективного времени выполнения 50% трудозатрат. В этом случае мы рекомендуем:

Также стоит помнить о производительности. Любые временные таблицы в запросах должны быть проиндексированы по полям, участвующим в соединениях. В нашем случае это поле День и поле с порядковым номером. Для этого после создания временной таблицы ВТ_ДанныеСНумерацией следует добавить инструкцию ИНДЕКСИРОВАТЬ ПО.

Использование системы компоновки данных (СКД) может еще сильнее упростить задачу. В СКД предусмотрены вычисляемые поля и агрегатные функции, такие как ПроцентВГруппе. Если отчет строится в СКД, можно вывести детальные записи и просто наложить отбор на системное поле процента, что избавит от необходимости писать сложные вложенные запросы с нумерацией.

← На главную