При разработке на платформе «1С:Предприятие» часто возникает задача проверить сам факт наличия записи в регистре накопления за определенный период. Типичный пример — проверка, был ли сотрудник на больничном в конкретный день, используя данные регистра ДанныеТабельногоУчетаРабочегоВремениСотрудников в конфигурации «Зарплата и управление персоналом». Рассмотрим ситуацию, когда программист обращается к виртуальной таблице .Обороты, но выбирает в секции ВЫБРАТЬ только измерения (например, Сотрудник) и периоды (День), полностью игнорируя ресурсы (Часы, Дни и т.д.).
Выясним, насколько такой подход оправдан с точки зрения производительности и архитектуры системы, и разберем альтернативные способы получения данных.
Прежде всего, проанализируем, что происходит «под капотом» платформы 1С, когда мы вызываем виртуальную таблицу .Обороты. Многие разработчики ошибочно полагают, что обращение к виртуальной таблице всегда быстрее, чем к физической. Однако это не так.
Когда мы пишем запрос к РегистрНакопления.ИмяРегистра.Обороты(...), платформа генерирует сложный SQL-запрос. Если мы не выбираем ресурсы, СУБД все равно вынуждена выполнять операцию группировки (GROUP BY) по всем измерениям, указанным в параметрах виртуальной таблицы, и по полям периодичности. Если наша цель — просто узнать факт наличия записи, то выполнение тяжелой операции группировки на стороне сервера SQL является избыточным расходом ресурсов.
Рассмотрим пример запроса, который вызывает сомнения:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДанныеТабельногоУчета.Сотрудник КАК Сотрудник,
| ДанныеТабельногоУчета.Период КАК День
|ИЗ
| РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников.Обороты(
| &НачалоПериода,
| &КонецПериода,
| День,
| ВидУчетаВремени = &ВидУчетаВремени) КАК ДанныеТабельногоУчета";
В данном случае использование виртуальной таблицы .Обороты с периодичностью День заставляет систему выполнять лишнюю работу. Проанализируем почему.
Основное преимущество виртуальных таблиц оборотов заключается в использовании механизма предварительно рассчитанных итогов. Эти итоги в 1С обычно хранятся с периодичностью «Месяц».
День (как в примере выше), платформа вынуждена обращаться к основной (физической) таблице движений регистра, чтобы «дособрать» данные за конкретные дни, которые не кратны месяцу.Таким образом, выбирая периодичность День, мы лишаем себя главного преимущества виртуальной таблицы (скорости итогов), но при этом оставляем накладные расходы на построение самой виртуальной таблицы платформой. В итоге запрос работает медленнее, чем прямое обращение к физической таблице.
Если нам не нужны агрегированные суммы ресурсов, а важен только факт наличия записи, правильнее обратиться к основной таблице регистра. Рассмотрим, как переписать исходный запрос для повышения производительности:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
| Табель.Сотрудник КАК Сотрудник,
| Табель.Период КАК День
|ИЗ
| РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Табель
|ГДЕ
| Табель.Период МЕЖДУ &НачалоПериода И &КонецПериода
| И Табель.ВидУчетаВремени = &ВидУчетаВремени";
Разберем преимущества этого подхода:
Периоду или по измерениям (например, Сотрудник или ВидУчетаВремени, если для них включено индексирование). Это позволяет выполнить операцию Index Seek вместо сканирования всей таблицы.GROUP BY с расчетом ресурсов.Если в коде нам не нужно получать список сотрудников или дат, а требуется лишь проверить, существует ли вообще хоть одна запись по заданным критериям (например, в рамках проверки в подписке на событие), можно использовать еще более оптимизированный вариант:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
| 1
|ИЗ
| РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Табель
|ГДЕ
| Табель.Сотрудник = &Сотрудник
| И Табель.Период = &НужнаяДата
| И Табель.ВидУчетаВремени = &ВидУчетаВремени";
Если НЕ Запрос.Выполнить().Пустой() Тогда
// Факт зафиксирован, выполняем логику
КонецЕсли;
Использование ВЫБРАТЬ ПЕРВЫЕ 1 вместе с обращением к физической таблице — это самый быстрый способ проверки существования данных в 1С.
Несмотря на вышесказанное, бывают ситуации, когда использование .Обороты без ресурсов оправдано:
НАЧАЛОПЕРИОДА(Табель.Период, ДЕНЬ) и ручную группировку.ГДЕ.Проанализировав ситуацию, мы пришли к следующим выводам:
РАЗЛИЧНЫЕ.Соблюдение этих простых правил позволит вашим запросам работать быстрее, а коду — быть прозрачнее для других разработчиков.