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