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

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

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

Разберем, почему не следует пытаться использовать для массовой выборки встроенный объект конфигурации КритерийОтбора, и как правильно построить быстрый и корректный запрос к базе данных.

Почему не подходит обращение к критерию отбора в запросе

Пользовательская структура подчиненности строится платформой на базе метаданных или объекта КритерийОтбора. Однако виртуальная таблица критерия отбора на уровне языка запросов 1С устроена так, что требует обязательной передачи конкретной ссылки в качестве параметра:


ВЫБРАТЬ
    СвязанныеДокументы.Ссылка КАК Ссылка
ИЗ
    КритерийОтбора.СвязанныеДокументы(&ТекущийДокумент) КАК СвязанныеДокументы

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

Базовый запрос: левое соединение и проверка на NULL

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

Посмотрим на базовый вариант запроса:


ВЫБРАТЬ
    Реализации.Ссылка КАК Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализации
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.МаршрутныйЛист.ТабличнаяЧасть КАК ТабМаршрутногоЛиста
        ПО Реализации.Ссылка = ТабМаршрутногоЛиста.Реализация
ГДЕ
    ТабМаршрутногоЛиста.Ссылка ЕСТЬ NULL

Если реализация уже указана в каком-либо маршрутном листе, то поле ТабМаршрутногоЛиста.Ссылка будет заполнено реальной ссылкой. Если же документа доставки для нее не создавалось, соединение подставит значение NULL, и условие в секции ГДЕ вернет именно нужные нам позиции.

Учет статуса подчиненного документа (проведение и пометка удаления)

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

Рассмотрим, как правильно отсечь неактуальные документы. Критически важное правило: условие проверки статуса подчиненного документа должно находиться строго в секции соединения ПО, а не в секции ГДЕ.


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

Если перенести проверку активности маршрутного листа в секцию ГДЕ, левое соединение неявно превратится во внутреннее, и отбор по ЕСТЬ NULL перестанет работать корректно.

Альтернативное решение: использование вложенного запроса с «НЕ В»

В зависимости от настроек СУБД (PostgreSQL или MS SQL Server) и объема таблиц, более высокую скорость выполнения может показать конструкция с отрицанием принадлежности списку (НЕ В). Оптимизаторы баз данных часто строят для таких запросов эффективные планы с использованием полусоединений (Anti Semi Join).

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


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

Оптимизация производительности на больших базах

При работе с базами, содержащими сотни тысяч документов, обратите внимание на следующие технические аспекты:

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

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

← На главную