При написании запросов к документам в 1С:Предприятии разработчики часто сталкиваются с необходимостью получить документ и подчиненную ему табличную часть в виде вложенной таблицы значений (например, для использования в механизмах выгрузки «Конвертации данных 2.x»). Однако при попытке отфильтровать строки такой вложенной таблицы с помощью условий в секции ГДЕ возникают неожиданные результаты. Разберем, как устроен этот механизм, какие ограничения накладывает платформа 1С и как правильно решить задачу фильтрации строк.
Рассмотрим типовую конструкцию запроса, в которой табличная часть выбирается через точку в основном списке полей:
ВЫБРАТЬ
Док.Дата КАК Дата,
Док.Номер КАК Номер,
Док.ТабЧасть.(
Ссылка,
НомерСтроки,
Реквизит
) КАК ТабЧасть
ИЗ
Документ.МойДокумент КАК Док
ГДЕ
Док.ТабЧасть.Реквизит = &Параметр
Проанализируем поведение платформы при выполнении такого запроса. Синтаксис языка запросов 1С при обращении к реквизиту табличной части в секции ГДЕ основного запроса транслируется на уровне СУБД в проверку существования (квантор существования EXISTS). Это означает следующее:
ГДЕ Док.ТабЧасть.Реквизит = &Параметр отбирает только сам документ-шапку, если в его табличной части есть хотя бы одна строка с нужным значением.ТабЧасть в результате запроса попадают все строки табличной части данного документа, включая те, которые не соответствуют переданному параметру. Отфильтровать строки внутри уже сформированной вложенной таблицы синтаксис языка запросов напрямую не позволяет.Обратим внимание на нюанс, с которым сталкиваются при попытке исключить строки. Выясним причину разницы между двумя похожими условиями:
ГДЕ Док.ТабЧасть.Реквизит <> 1 проверяет: «Существует ли в табличной части хотя бы одна строка, где реквизит не равен 1». Если в документе есть 5 строк с Реквизит = 1 и 1 строка с Реквизит = 2, документ попадет в выборку целиком со всеми 6 строками.ГДЕ НЕ Док.ТабЧасть.Реквизит = 1 проверяет: «Не существует ни одной строки, где реквизит равен 1». Документ попадет в результат только в том случае, если в нем вообще отсутствуют строки со значением 1.Если задача заключается в получении плоской таблицы, содержащей строго нужные строки табличной части и данные шапки документа, правильнее строить запрос напрямую к таблице табличной части:
ВЫБРАТЬ
Док.Ссылка.Дата КАК Дата,
Док.Ссылка.Номер КАК Номер,
Док.Ссылка КАК Ссылка,
Док.НомерСтроки КАК НомерСтроки,
Док.Реквизит КАК Реквизит
ИЗ
Документ.МойДокумент.ТабЧасть КАК Док
ГДЕ
Док.Реквизит = &Параметр
В этом случае условие ГДЕ накладывается на конкретные физические записи таблицы, и в результат попадут только те строки, которые полностью соответствуют условию отбора.
Если требуется сохранить двухуровневую иерархическую структуру «Документ — Строки ТЧ», оптимальным решением является использование секции ИТОГИ:
ВЫБРАТЬ
Док.Ссылка КАК Ссылка,
Док.Ссылка.Дата КАК Дата,
Док.Ссылка.Номер КАК Номер,
Док.НомерСтроки КАК НомерСтроки,
Док.Реквизит КАК Реквизит
ИЗ
Документ.МойДокумент.ТабЧасть КАК Док
ГДЕ
Док.Реквизит = &Параметр
ИТОГИ ПО
Ссылка
Разберем программный обход такого результата в коде 1С:
ВыборкаДокументов = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДокументов.Следующий() Цикл
// Доступ к полям шапки документа
ТекущийДокумент = ВыборкаДокументов.Ссылка;
// Вложенная выборка только отфильтрованных строк ТЧ
ВыборкаСтрок = ВыборкаДокументов.Выбрать();
Пока ВыборкаСтрок.Следующий() Цикл
// Обработка строки, где Реквизит равен заданному параметру
НомерСтроки = ВыборкаСтрок.НомерСтроки;
КонецЦикла;
КонецЦикла;
Если запрос формируется в рамках правил выгрузки конфигурации «1С:Конвертация данных 2.x», где платформа ожидает наличие вложенной таблицы в колонке, обойти ограничение запроса можно встроенными событиями КД:
ОбъектКоллекции:
Если ОбъектКоллекции.Реквизит <> 1 Тогда
Отказ = Истина;
КонецЕсли;
ВыборкаДанных.В случаях, когда объект документа уже прочитан в память или получен в виде полной таблицы, отбор строк по значению реквизита выполняется штатными средствами коллекций без повторного обращения к СУБД:
Отбор = Новый Структура("Реквизит", 1);
ОтфильтрованныеСтроки = ДокументОбъект.ТабЧасть.НайтиСтроки(Отбор);
Для Каждого СтрокаТЧ Из ОтфильтрованныеСтроки Цикл
// Работа с отобранными строками
КонецЦикла;