При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с задачей вывода дополнительных реквизитов (созданных в режиме «1С:Предприятие») непосредственно в список справочника или документа. Однако стандартное добавление таких реквизитов в текст запроса динамического списка часто приводит к неприятному эффекту: строки начинают дублироваться. Разберем по шагам, почему это происходит и как правильно изменить запрос, чтобы данные отображались корректно.
Проанализируем ситуацию. Дополнительные реквизиты в типовых конфигурациях (таких как Управление торговлей 11, ERP или Комплексная автоматизация) хранятся не в полях основной таблицы справочника, а в табличной части под названием ДополнительныеРеквизиты. Каждый дополнительный реквизит — это отдельная строка в этой табличной части.
Когда мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ основной таблицы справочника Контрагенты с его табличной частью ДополнительныеРеквизиты, система находит для одного контрагента столько строк, сколько у него заполнено дополнительных реквизитов. В результате в выборку попадает не одна строка с контрагентом, а целая «пачка» строк, в каждой из которых указано значение только одного из реквизитов. Это классическая проблема умножения строк при соединении «один ко многим».
Рассмотрим пример ошибочного подхода, который часто используют новички:
// ОШИБОЧНЫЙ ПРИМЕР
ВЫБРАТЬ
СправочникКонтрагенты.Ссылка КАК Ссылка,
ВЫБОР КОГДА КонтрагентыДополнительныеРеквизиты.Свойство.Наименование = "Закрыт"
ТОГДА КонтрагентыДополнительныеРеквизиты.Значение КОНЕЦ КАК Закрыт
ИЗ
Справочник.Контрагенты КАК СправочникКонтрагенты
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.ДополнительныеРеквизиты КАК КонтрагентыДополнительныеРеквизиты
ПО КонтрагентыДополнительныеРеквизиты.Ссылка = СправочникКонтрагенты.Ссылка
В этом случае, если у контрагента «Альфа» заполнено пять разных дополнительных реквизитов, в динамическом списке вы увидите «Альфа» пять раз. В одной строке будет заполнено поле «Закрыт», а в остальных четырех — пусто (NULL).
Самый простой и эффективный способ решения проблемы «в лоб» — это выполнение отдельного соединения для каждого конкретного реквизита. Рассмотрим подробнее, как это реализовать. Нам нужно присоединить табличную часть столько раз, сколько реквизитов мы хотим вывести, причем в условии связи (ПО) необходимо сразу указать, какое именно свойство мы ищем.
Проанализируем исправленный код запроса для динамического списка:
ВЫБРАТЬ
СправочникКонтрагенты.Ссылка КАК Ссылка,
СправочникКонтрагенты.Наименование КАК Наименование,
// Выбираем значения из разных псевдонимов таблиц
ЕСТЬNULL(ДопРеквизитЗакрыт.Значение, ЛОЖЬ) КАК Закрыт,
ЕСТЬNULL(ДопРеквизитРабот.Значение, ЛОЖЬ) КАК ВРазработке,
ЕСТЬNULL(ДопРеквизитНомерРеестра.Значение, 0) КАК НомерРеестра
ИЗ
Справочник.Контрагенты КАК СправочникКонтрагенты
// Первое соединение только для свойства "закрыт"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизитЗакрыт
ПО (ДопРеквизитЗакрыт.Ссылка = СправочникКонтрагенты.Ссылка
И ДопРеквизитЗакрыт.Свойство.Наименование = "закрыт")
// Второе соединение только для свойства "работ"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизитРабот
ПО (ДопРеквизитРабот.Ссылка = СправочникКонтрагенты.Ссылка
И ДопРеквизитРабот.Свойство.Наименование = "работ")
// Третье соединение только для свойства "Номер_Реестра"
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизитНомерРеестра
ПО (ДопРеквизитНомерРеестра.Ссылка = СправочникКонтрагенты.Ссылка
И ДопРеквизитНомерРеестра.Свойство.Наименование = "Номер_Реестра")
Важный момент: использование функции ЕСТЬNULL позволяет нам заменить пустое значение (когда реквизит у контрагента вообще не создан) на значение по умолчанию (Ложь, 0 или пустую строку). Это предотвращает ошибки отображения в интерфейсе.
В платформе 1С существует штатный механизм для работы с дополнительными реквизитами, который называется Характеристики. Если правильно настроить этот механизм в метаданных динамического списка, нам вообще не придется вручную писать JOIN в запросе.
Разберем по шагам, как это работает:
ДинамическийСписок.ДополнительныеРеквизитыИСведения).ДополнительныеРеквизиты объекта).Этот способ является более «правильным» с точки зрения архитектуры 1С, так как система сама оптимизирует запрос к базе данных и исключает дублирование строк на уровне платформы.
Выясним причину возможных проблем при реализации первого способа. При написании запроса вручную следует обратить внимание на следующие аспекты:
1. Использование ПОДОБНО. В коде на форуме часто встречается условие ПОДОБНО "%закрыт%". Это плохая практика для динамического списка. Поиск подстроки с процентом в начале делает невозможным использование индексов SQL-сервера. Если в вашей базе десятки тысяч контрагентов, список начнет заметно «тормозить». Рекомендуем использовать точное совпадение по наименованию или, что еще лучше, поиск по ссылке на свойство, если она известна (через параметры запроса).
2. Контрагенты vs Партнеры. В конфигурации «Управление торговлей 11» существует разделение на сущности Партнеры и Контрагенты. Дополнительные реквизиты могут быть назначены как одному, так и другому объекту. Если вы добавили реквизит партнеру, а пытаетесь достать его из табличной части контрагента — вы получите пустое значение. Всегда проверяйте, к какому именно набору свойств привязан ваш реквизит в режиме «1С:Предприятие».
3. Типизация значений. Поле Значение в табличной части доп. реквизитов имеет составной тип. Если вы выводите числовой реквизит (например, «Номер реестра»), полезно явно привести его к типу в запросе, чтобы функции сортировки и группировки в списке работали корректно:
ВЫРАЗИТЬ(ДопРеквизитНомерРеестра.Значение КАК ЧИСЛО(15, 0))
Подводя итог, можно сказать: чтобы избежать дублей, никогда не соединяйте таблицу справочника с табличной частью дополнительных реквизитов «просто так». Либо используйте механизм характеристик платформы, либо делайте точечные левые соединения для каждого конкретного свойства с обязательным указанием фильтра по свойству в условии ПО.
Проанализировав ситуацию и применив предложенные методы, вы получите чистый, быстрый и удобный интерфейс для работы пользователей с дополнительными данными.