Как в запросе 1С получить реквизит из составного поля «ДокументОснование» без потери производительности?

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

В разработке на платформе «1С:Предприятие» одной из классических задач является получение реквизита из поля составного типа данных. Типичный пример — документ СчетФактура, у которого реквизит ДокументОснование может принимать значения различных типов (например, ВозвратТоваровОтПокупателя, ВозвратТоваровПоставщику, ПоступлениеТоваровУслуг). У каждого из этих документов-оснований есть реквизит ПунктРазгрузки, который нам необходимо вывести в запросе одной общей колонкой.

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

В чем опасность простого обращения «через точку»?

В процессе обсуждения на форумах часто звучит совет: «Просто напишите в запросе СчетФактура.ДокументОснование.ПунктРазгрузки и не мучайтесь». Давайте проанализируем, почему этот совет вреден и опасен для рабочих баз данных.

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

В типовых конфигурациях (таких как ERP, Комплексная автоматизация или Управление торговлей) список возможных типов для основания счета-фактуры может включать десятки документов. В результате один простой запрос превращается на уровне СУБД в огромного «монстра» с тридцатью и более левыми соединениями. Это приводит к следующим проблемам:

Поэтому обращение «через точку» к реквизитам составного типа в реальных задачах использовать категорически нельзя. Рассмотрим, как решить задачу правильно.

Способ 1. Использование временной таблицы и ОБЪЕДИНИТЬ ВСЕ (Решение автора)

Этот способ интуитивно нашел автор темы, и он является одним из самых надежных и производительных. Мы выбираем данные только из тех таблиц-оснований, которые нам реально необходимы, помещаем их во временную таблицу, а затем связываем с основным документом.

Рассмотрим этот метод по шагам:

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

Посмотрим на пример реализации этого запроса:


ВЫБРАТЬ
    ВозвратПокупателя.Ссылка КАК Ссылка,
    ВозвратПокупателя.ПунктРазгрузки КАК ПунктРазгрузки
ПОМЕСТИТЬ ВТ_ПунктыРазгрузки
ИЗ
    Документ.ВозвратТоваровОтПокупателя КАК ВозвратПокупателя

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

ВЫБРАТЬ
    ВозвратПоставщику.Ссылка,
    ВозвратПоставщику.ПунктРазгрузки
ИЗ
    Документ.ВозвратТоваровПоставщику КАК ВозвратПоставщику

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

ВЫБРАТЬ
    Поступление.Ссылка,
    Поступление.ПунктРазгрузки
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Поступление

ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СчетФактура.Ссылка КАК Ссылка,
    СчетФактура.Номер КАК Номер,
    СчетФактура.Дата КАК Дата,
    СчетФактура.ДокументОснование КАК ДокументОснование,
    ЕСТЬNULL(ВТ_ПунктыРазгрузки.ПунктРазгрузки, ЗНАЧЕНИЕ(Справочник.ПунктыРазгрузки.ПустаяСсылка)) КАК ПунктРазгрузки
ИЗ
    Документ.СчетФактура КАК СчетФактура
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПунктыРазгрузки КАК ВТ_ПунктыРазгрузки
        ПО СчетФактура.ДокументОснование = ВТ_ПунктыРазгрузки.Ссылка

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

Способ 2. Явное приведение типов с помощью ВЫРАЗИТЬ и ВЫБОР (Стандарт 1С)

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

Рассмотрим структуру такого запроса. Мы используем конструкцию ВЫБОР КОГДА ... ТОГДА для проверки типа документа-основания с помощью оператора ССЫЛКА. Если тип совпадает, мы явно выражаем поле через функцию ВЫРАЗИТЬ и получаем его реквизит.

Посмотрим на практический пример:


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

Преимущества метода: Это лаконичный и красивый запрос, который пишется в одно действие без создания временных таблиц. Платформа «1С» на уровне СУБД создаст левые соединения только с тремя таблицами, указанными внутри функций ВЫРАЗИТЬ. Производительность такого решения находится на высоком уровне.

Способ 3. Прямое левое соединение с таблицами документов и ЕСТЬNULL

Если вам по какой-то причине не хочется использовать временные таблицы или громоздкую конструкцию ВЫБОР, можно связать исходную таблицу СчетФактура напрямую с тремя физическими таблицами нужных документов при помощи левых соединений.

Для того чтобы «собрать» значения реквизита ПунктРазгрузки из разных таблиц в одно поле результирующей выборки, мы применим вложенную цепочку функций ЕСТЬNULL. Функция проверяет: если в первой таблице значение равно NULL (соединение не состоялось), то берется значение из второй таблицы, если и там NULL — из третьей.

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


ВЫБРАТЬ
    СчетФактура.Ссылка КАК Ссылка,
    СчетФактура.Номер КАК Номер,
    СчетФактура.Дата КАК Дата,
    СчетФактура.ДокументОснование КАК ДокументОснование,
    ЕСТЬNULL(ВозвратПокупателя.ПунктРазгрузки,
        ЕСТЬNULL(ВозвратПоставщику.ПунктРазгрузки,
            ЕСТЬNULL(Поступление.ПунктРазгрузки, ЗНАЧЕНИЕ(Справочник.ПунктыРазгрузки.ПустаяСсылка)))) КАК ПунктРазгрузки
ИЗ
    Документ.СчетФактура КАК СчетФактура
    
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтПокупателя КАК ВозвратПокупателя
        ПО СчетФактура.ДокументОснование = ВозвратПокупателя.Ссылка
        
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровПоставщику КАК ВозвратПоставщику
        ПО СчетФактура.ДокументОснование = ВозвратПоставщику.Ссылка
        
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг КАК Поступление
        ПО СчетФактура.ДокументОснование = Поступление.Ссылка

Преимущества метода: Четко и наглядно видны связи между таблицами. Запрос легко читать и отлаживать. С точки зрения производительности этот вариант аналогичен предыдущим, так как количество соединений жестко ограничено разработчиком на уровне текста запроса.

Подведем итоги: какой метод выбрать?

Мы детально проанализировали три отличных способа решения задачи получения реквизита из составного поля. Все три варианта являются абсолютно рабочими, безопасными и производительными. Однако у каждого из них есть свои особенности использования:

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

← На главную