Как в запросе 1С отобрать документы по дате, если поле имеет составной ссылочный тип

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

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

Проблема разыменования составного типа через точку

Рассмотрим, что происходит, когда в тексте запроса мы обращаемся к реквизиту поля с составным типом напрямую через точку, например Таблица.СторнируемыйДокумент.Дата. Конструктор запросов позволяет выбрать общее поле Дата, однако на уровне СУБД этот код транслируется в ресурсоемкую конструкцию:

  1. Платформа 1С автоматически генерирует соединения LEFT OUTER JOIN со всеми таблицами документов, входящими в составной тип. Если в метаданных в тип включено 50 видов документов, в SQL-запросе появится 50 соединений.
  2. Значение результирующего поля вычисляется через разветвленное выражение CASE WHEN по типу ссылки.
  3. Оптимизатор СУБД может превысить лимиты на сложность плана выполнения или отказаться от использования индексов, переходя на полное сканирование таблиц (Table/Index Scan), что существенно замедляет выполнение запроса.

Проанализируем основные способы решения этой задачи — от явного ограничения типов в запросе до оптимизации структуры хранения данных.

Способ 1. Явное соединение по типам с использованием объединения

Если нам точно известно, какие виды документов могут корректироваться, наиболее контролируемым и быстрым с точки зрения СУБД решением является разделение выборки по конкретным таблицам с последующим объединением результатов через ОБЪЕДИНИТЬ ВСЕ.

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


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

ОБЪЕДИНИТЬ ВСЕ

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

В этом случае СУБД обращается только к указанным таблицам, задействуя индекс по полю Дата, что обеспечивает максимальное быстродействие.

Способ 2. Ограничение типов через конструкцию ВЫРАЗИТЬ и ССЫЛКА

Если разделение на отдельные ветки ОБЪЕДИНИТЬ ВСЕ делает запрос слишком громоздким, можно использовать явное приведение типов в секции ВЫБОР. Это исключит соединение со всеми остальными неиспользуемыми таблицами базы данных.

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


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

ВЫБРАТЬ
    ВТ.Корректировка,
    ВТ.СторнируемыйДокумент
ИЗ
    ВТ_ДанныеСДатами КАК ВТ
ГДЕ
    ВТ.ДатаСторнируемогоДокумента МЕЖДУ &ДатаДокументовС И &ДатаДокументовПо

Способ 3. Использование таблицы общего журнала документов

Если все или большинство сторнируемых документов входят в общий журнал документов (например, ЖурналДокументов.СкладскиеДокументы), мы можем использовать таблицу этого журнала. У журналов документов в 1С есть собственная таблица в базе данных, содержащая поля Ссылка, Дата и Проведен для всех входящих в журнал видов документов.

Разберем по шагам построение запроса через журнал документов:


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

Такой подход позволяет за одно внутреннее соединение получить дату любого входящего в журнал документа без генерации десятков LEFT JOIN.

Способ 4. Выборка через временную таблицу уникальных ссылок

Когда документов в регистре или табличной части много, частое обращение через точку к повторяющимся ссылкам многократно нагружает СУБД. Чтобы оптимизировать работу, вынесем уникальные ссылки во временную таблицу перед наложением условий:

  1. Выбираем список уникальных ссылок корректируемых документов во временную таблицу ВТ_УникальныеДокументы с помощью ключевого слова РАЗЛИЧНЫЕ.
  2. Соединяем временную таблицу с целевыми данными и накладываем фильтр по дате.

Посмотрим на пример запроса:


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

ВЫБРАТЬ
    ВТ.Документ КАК Документ
ПОМЕСТИТЬ ВТ_ОтобранныеДокументы
ИЗ
    ВТ_УникальныеДокументы КАК ВТ
ГДЕ
    ВТ.Документ.Дата МЕЖДУ &ДатаДокументовС И &ДатаДокументовПо

Способ 5. Хранение даты в реквизитах табличной части

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

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


ВЫБРАТЬ
    Таблица.Ссылка КАК Корректировка,
    Таблица.СторнируемыйДокумент КАК Документ
ИЗ
    Документ.КорректировкаЗаписейРегистров.ТаблицаСторно КАК Таблица
ГДЕ
    Таблица.Ссылка.Дата МЕЖДУ &ПериодКорректировокС И &ПериодКорректировокПо
    И Таблица.ДатаСторнируемогоДокумента МЕЖДУ &ДатаДокументовС И &ДатаДокументовПо

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

При выборе метода оптимизации запроса следует ориентироваться на следующие правила:

  1. Не использовать прямое разыменование через точку для полей с широким составным типом без предварительного ограничения типов или фильтрации по временной таблице.
  2. Использовать журнал документов, если все целевые документы включены в единый журнал.
  3. Применять объединение запросов (ОБЪЕДИНИТЬ ВСЕ) при небольшом известном количестве видов документов.
  4. Дублировать дату в реквизит, если операция выполняется часто и требует максимального быстродействия.
← На главную