Как правильно наложить условие на табличную часть в запросе 1С?

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

При написании запросов к документам в 1С:Предприятии разработчики часто сталкиваются с необходимостью получить документ и подчиненную ему табличную часть в виде вложенной таблицы значений (например, для использования в механизмах выгрузки «Конвертации данных 2.x»). Однако при попытке отфильтровать строки такой вложенной таблицы с помощью условий в секции ГДЕ возникают неожиданные результаты. Разберем, как устроен этот механизм, какие ограничения накладывает платформа 1С и как правильно решить задачу фильтрации строк.

В чем заключается проблема выборки через вложенную таблицу

Рассмотрим типовую конструкцию запроса, в которой табличная часть выбирается через точку в основном списке полей:


ВЫБРАТЬ
    Док.Дата КАК Дата,
    Док.Номер КАК Номер,
    Док.ТабЧасть.(
        Ссылка,
        НомерСтроки,
        Реквизит
    ) КАК ТабЧасть
ИЗ
    Документ.МойДокумент КАК Док
ГДЕ
    Док.ТабЧасть.Реквизит = &Параметр

Проанализируем поведение платформы при выполнении такого запроса. Синтаксис языка запросов 1С при обращении к реквизиту табличной части в секции ГДЕ основного запроса транслируется на уровне СУБД в проверку существования (квантор существования EXISTS). Это означает следующее:

  1. Условие ГДЕ Док.ТабЧасть.Реквизит = &Параметр отбирает только сам документ-шапку, если в его табличной части есть хотя бы одна строка с нужным значением.
  2. Во вложенную таблицу ТабЧасть в результате запроса попадают все строки табличной части данного документа, включая те, которые не соответствуют переданному параметру. Отфильтровать строки внутри уже сформированной вложенной таблицы синтаксис языка запросов напрямую не позволяет.

Особенности кванторной логики при отрицании

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

  1. Условие ГДЕ Док.ТабЧасть.Реквизит <> 1 проверяет: «Существует ли в табличной части хотя бы одна строка, где реквизит не равен 1». Если в документе есть 5 строк с Реквизит = 1 и 1 строка с Реквизит = 2, документ попадет в выборку целиком со всеми 6 строками.
  2. Условие ГДЕ НЕ Док.ТабЧасть.Реквизит = 1 проверяет: «Не существует ни одной строки, где реквизит равен 1». Документ попадет в результат только в том случае, если в нем вообще отсутствуют строки со значением 1.

Способ 1. Прямой запрос к реальной таблице табличной части

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


ВЫБРАТЬ
    Док.Ссылка.Дата КАК Дата,
    Док.Ссылка.Номер КАК Номер,
    Док.Ссылка КАК Ссылка,
    Док.НомерСтроки КАК НомерСтроки,
    Док.Реквизит КАК Реквизит
ИЗ
    Документ.МойДокумент.ТабЧасть КАК Док
ГДЕ
    Док.Реквизит = &Параметр

В этом случае условие ГДЕ накладывается на конкретные физические записи таблицы, и в результат попадут только те строки, которые полностью соответствуют условию отбора.

Способ 2. Использование итогов и обхода по группировкам

Если требуется сохранить двухуровневую иерархическую структуру «Документ — Строки ТЧ», оптимальным решением является использование секции ИТОГИ:


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

Разберем программный обход такого результата в коде 1С:


ВыборкаДокументов = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДокументов.Следующий() Цикл
    // Доступ к полям шапки документа
    ТекущийДокумент = ВыборкаДокументов.Ссылка;
    
    // Вложенная выборка только отфильтрованных строк ТЧ
    ВыборкаСтрок = ВыборкаДокументов.Выбрать();
    Пока ВыборкаСтрок.Следующий() Цикл
        // Обработка строки, где Реквизит равен заданному параметру
        НомерСтроки = ВыборкаСтрок.НомерСтроки;
    КонецЦикла;
КонецЦикла;

Способ 3. Фильтрация данных при выгрузке в «Конвертации данных 2.x»

Если запрос формируется в рамках правил выгрузки конфигурации «1С:Конвертация данных 2.x», где платформа ожидает наличие вложенной таблицы в колонке, обойти ограничение запроса можно встроенными событиями КД:

  1. Фильтрация в ПКГС (Правило конвертации группы свойств): во вложенном обработчике «Перед выгрузкой» для строки табличной части обратимся к параметру ОбъектКоллекции:
    
    Если ОбъектКоллекции.Реквизит <> 1 Тогда
        Отказ = Истина;
    КонецЕсли;
    
    При таком подходе запрос может выбирать документ с полной табличной частью, а неподходящие строки будут отсекаться непосредственно механизмом выгрузки.
  2. Использование произвольного алгоритма в ПВД: в событии «Перед обработкой» правила выгрузки данных можно выполнить запрос к плоской таблице с итогами, программно сформировать структуру с подчиненными отфильтрованными таблицами значений и передать ее в коллекцию ВыборкаДанных.

Способ 4. Программный отбор строк через метод «НайтиСтроки()»

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


Отбор = Новый Структура("Реквизит", 1);
ОтфильтрованныеСтроки = ДокументОбъект.ТабЧасть.НайтиСтроки(Отбор);

Для Каждого СтрокаТЧ Из ОтфильтрованныеСтроки Цикл
    // Работа с отобранными строками
КонецЦикла;
← На главную