Как настроить отбор по регистратору в запросе 1С и избежать избыточных строк

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

При работе с регистрами накопления или сведений в системе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда простой запрос к виртуальной таблице оборотов или движений возвращает лавинообразный объем данных — сотни и тысячи лишних строк вместо нескольких ожидаемых записей. Особенно остро эта проблема проявляется, когда требуется получить данные в разрезе регистратора (документа, сделавшего запись), но при этом отфильтровать выборку по конкретному типу документа или по конкретному экземпляру.

В этой статье мы подробно проанализируем, почему возникает избыточность данных при работе с полем Регистратор, разберем типичные ошибки начинающих разработчиков и рассмотрим профессиональные методы оптимизации таких запросов. Мы научимся правильно использовать параметры виртуальных таблиц, избавляться от тяжелых неявных соединений с помощью функции ВЫРАЗИТЬ и элегантно решать конфликт между периодом запроса и выбранным документом-параметром.

Выясняем причину: почему запрос выводит сотни лишних строк?

Рассмотрим классическую ситуацию. Разработчику необходимо получить движения регистра накопления ПартииТоваровОрганизаций за определенный день, в который был проведен всего один документ ПриобретениеТоваровУслуг, содержащий две позиции номенклатуры. Ожидаемый результат в консоли запросов — ровно две строки. Однако на практике запрос возвращает более 500 строк.

Причина такого поведения кроется в особенностях работы виртуальных таблиц (например, таблицы Обороты) и механизме обработки условий платформой 1С и СУБД. Если в секции ВЫБРАТЬ запроса используется конструкция ВЫБОР для фильтрации данных по типу регистратора, СУБД все равно вынуждена обработать абсолютно все записи регистра за указанный период. Конструкция вида ВЫБОР КОГДА Регистратор ССЫЛКА Документ.ПриобретениеТоваровУслуг... лишь зануляет или скрывает значения в неподходящих строках, но не исключает сами строки из результирующей выборки. В итоге мы получаем сотни строк с пустыми значениями или избыточными данными из других документов.

Решение 1. Быстрый обход проблемы через секцию ГДЕ (Базовый уровень)

Первое и самое очевидное решение, которое помогает быстро исправить количество строк в результате — это перенос условия фильтрации по типу документа из конструкции ВЫБОР непосредственно в секцию ГДЕ запроса.

Проанализируем пример такого запроса:


ВЫБРАТЬ
    ПартииТоваровОрганизацийОбороты.Период КАК Период,
    ПартииТоваровОрганизацийОбороты.Регистратор КАК Регистратор,
    ПартииТоваровОрганизацийОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровОрганизацийОбороты.КоличествоОборот КАК Количество
ИЗ
    РегистрНакопления.ПартииТоваровОрганизаций.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК ПартииТоваровОрганизацийОбороты
ГДЕ
    ПартииТоваровОрганизацийОбороты.Регистратор ССЫЛКА Документ.ПриобретениеТоваровУслуг

Почему это работает: Добавление жесткого фильтра в секцию ГДЕ заставляет СУБД отсечь все записи, у которых регистратор не является документом поступления. На выходе мы получим ровно те 2 строки, которые ожидали увидеть.

В чем опасность этого подхода: Несмотря на то, что визуальный результат достигнут, с точки зрения производительности этот метод является серьезным антипаттерном. При выполнении такого запроса СУБД сначала строит полную виртуальную таблицу оборотов по всем типам документов за указанный период (выполняя чтение избыточных данных с диска или из кэша), и только после этого накладывает фильтр ГДЕ. На крупных базах данных с высокой интенсивностью транзакций такой запрос вызовет колоссальные задержки и избыточную нагрузку на сервер.

Решение 2. Профессиональная оптимизация через параметры виртуальной таблицы

Чтобы запрос работал максимально быстро, мы должны передавать условия фильтрации непосредственно в параметры виртуальной таблицы. Для таблицы Обороты четвертым параметром является Условие. Передавая фильтр туда, мы заставляем платформу 1С наложить ограничение на этапе формирования SQL-запроса к базе данных.

Давайте разберем по шагам, как должен выглядеть правильный и оптимизированный запрос:


ВЫБРАТЬ
    ПартииТоваровОрганизацийОбороты.Период КАК Период,
    ПартииТоваровОрганизацийОбороты.Регистратор КАК Регистратор,
    ПартииТоваровОрганизацийОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровОрганизацийОбороты.КоличествоОборот КАК Количество
ИЗ
    РегистрНакопления.ПартииТоваровОрганизаций.Обороты(
        &НачалоПериода, 
        &КонецПериода, 
        Регистратор, 
        Регистратор ССЫЛКА Документ.ПриобретениеТоваровУслуг
    ) КАК ПартииТоваровОрганизацийОбороты

При таком подходе СУБД выбирает из физических таблиц базы данных только те записи, которые изначально соответствуют заданному типу регистратора. Это исключает сканирование лишних индексов и таблиц документов других типов, существенно сокращая время выполнения запроса.

Решение 3. Избавление от неявных соединений с помощью функции ВЫРАЗИТЬ

Поле Регистратор в регистрах 1С имеет составной тип данных. Оно может ссылаться на десятки различных видов документов, определенных в конфигурации как регистраторы для данного регистра. Когда мы просто выбираем поле Регистратор или пытаемся обратиться к его реквизитам через точку (например, Регистратор.Организация), СУБД генерирует тяжелые неявные левые соединения (LEFT JOIN) со всеми таблицами документов, входящих в этот составной тип.

Это приводит к резкому падению производительности. Чтобы предотвратить образование десятков неявных соединений в результирующем SQL-запросе, мы обязаны использовать функцию ВЫРАЗИТЬ. Она жестко указывает платформе и СУБД, к таблице какого конкретно документа необходимо обратиться.

Посмотрим на пример применения функции ВЫРАЗИТЬ:


ВЫБРАТЬ
    ПартииТоваровОрганизацийОбороты.Период КАК Период,
    ВЫРАЗИТЬ(ПартииТоваровОрганизацийОбороты.Регистратор КАК Документ.ПриобретениеТоваровУслуг) КАК Регистратор,
    ПартииТоваровОрганизацийОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровОрганизацийОбороты.КоличествоОборот КАК Количество
ИЗ
    РегистрНакопления.ПартииТоваровОрганизаций.Обороты(
        &НачалоПериода, 
        &КонецПериода, 
        Регистратор, 
        Регистратор ССЫЛКА Документ.ПриобретениеТоваровУслуг
    ) КАК ПартииТоваровОрганизацийОбороты

Используя ВЫРАЗИТЬ(Регистратор КАК Документ.ПриобретениеТоваровУслуг), мы гарантируем, что СУБД выполнит соединение только с одной таблицей — _Document{ID} (таблицей документа поступления), проигнорировав все остальные типы документов. Это ключевое правило разработки высоконагруженных систем на платформе 1С.

Решение 4. Разрешение конфликта между периодом и выбранным документом

Часто перед разработчиком встает задача: предоставить пользователю возможность выбирать конкретный документ-регистратор в качестве параметра отчета. Однако здесь возникает логический конфликт. Если пользователь задаст период (например, январь), но в качестве параметра выберет документ за февраль, запрос вернет пустой результат, что вызовет недоумение у конечного пользователя.

Рассмотрим три эффективных архитектурных способа решения этой проблемы.

Способ А: Использование Системы компоновки данных (СКД)

Если ваш отчет или обработка строятся на базе СКД, лучшим решением будет использование фигурных скобок для создания необязательных условий. Язык компоновки данных позволяет гибко управлять параметрами и отборами.

В тексте запроса внутри параметров виртуальной таблицы или в секции ГДЕ мы можем прописать расширение языка запросов СКД:

{ГДЕ Регистратор.* КАК Регистратор}

Если пользователь заполнит отбор по регистратору на форме отчета, СКД автоматически внедрит этот фильтр в исполняемый SQL-запрос. Если же отбор останется пустым, платформа просто проигнорирует данное условие, и отчет сформируется по стандартным правилам за выбранный период.

Способ Б: Логическое условие "ИЛИ" в параметрах запроса

Если запрос выполняется программно без использования СКД, мы можем сделать параметр выбора конкретного документа необязательным с помощью булевой логики. Для этого передадим в параметр пустую ссылку нужного типа, если документ не выбран пользователем.

Посмотрим на пример реализации такого условия в параметрах виртуальной таблицы:


РегистрНакопления.ПартииТоваровОрганизаций.Обороты(
    &НачалоПериода, 
    &КонецПериода, 
    Регистратор, 
    (&ДокументСсылка = ЗНАЧЕНИЕ(Документ.ПриобретениеТоваровУслуг.ПустаяСсылка) 
        ИЛИ Регистратор = &ДокументСсылка)
) КАК ПартииТоваровОрганизацийОбороты

Разберем логику: Если пользователь не выбрал конкретный документ, мы передаем в параметр &ДокументСсылка пустую ссылку. В этом случае первая часть условия &ДокументСсылка = ЗНАЧЕНИЕ(...) возвращает значение ИСТИНА, и фильтрация по конкретному документу отключается — запрос возвращает все движения за период. Если же документ выбран, срабатывает вторая часть условия, возвращая строго движения по выбранному регистратору.

Способ В: Программная предобработка периода на форме

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

Рассмотрим пример кода на встроенном языке:


Если ЗначениеЗаполнено(ДокументПараметр) Тогда
    // Автоматически устанавливаем период в границы выбранного документа
    ПараметрНачалоПериода = НачалоДня(ДокументПараметр.Дата);
    ПараметрКонецПериода = КонецДня(ДокументПараметр.Дата);
    
    // Блокируем поля выбора периода на форме, чтобы избежать пользовательских ошибок
    Элементы.ФормаПериод.Доступность = Ложь;
Иначе
    // Если документ не выбран, возвращаем стандартное управление периодом
    Элементы.ФормаПериод.Доступность = Истина;
КонецЕсли;

Этот подход полностью исключает возникновение логических конфликтов данных, так как программа самостоятельно синхронизирует период отчета с датой проведения интересующего пользователя документа.

Резюме и рекомендации

Подведем итог и сформулируем ключевые правила для работы с регистраторами в запросах 1С:

  1. Никогда не фильтруйте типы документов через ВЫБОР КОГДА в секции ВЫБРАТЬ, если вам нужно просто уменьшить количество строк. Это приводит к получению сотен пустых строк.
  2. Избегайте фильтрации по регистратору в секции ГДЕ без крайней необходимости. Переносите все условия отбора непосредственно в параметры виртуальной таблицы (четвертый параметр).
  3. Всегда используйте функцию ВЫРАЗИТЬ при обращении к полям или реквизитам составного типа Регистратор. Это защитит ваш запрос от десятков скрытых и неэффективных левых соединений (LEFT JOIN) на уровне СУБД.
  4. Используйте СКД или гибкую булеву логику для обработки сценариев, когда пользователь может одновременно выбирать и период, и конкретный документ, чтобы исключить конфликты параметров и пустые результаты отчетов.
← На главную