При разработке интерфейсов на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью вывести дополнительные реквизиты или сведения в форму списка. Однако стандартное левое соединение с регистром сведений ДополнительныеСведения в запросе динамического списка часто приводит к критической ошибке: "Обнаружено дублирование ключевых значений в колонках". Это происходит потому, что одному объекту (например, документу или справочнику) в регистре соответствует несколько записей с разными свойствами, и динамический список получает несколько строк с одинаковой ссылкой, что недопустимо для его корректной работы.
В данной статье мы подробно разберем, как «склеить» эти строки в одну, чтобы каждое свойство отображалось в своей колонке, а динамический список работал стабильно. Мы проанализируем ситуацию, выясним причины проблемы и рассмотрим несколько эффективных способов решения.
Динамический список требует наличия уникального ключа для каждой строки (обычно это поле Ссылка основной таблицы). Если в тексте запроса указана ОсновнаяТаблица, платформа ожидает, что результат запроса будет содержать уникальные ссылки. Когда мы присоединяем таблицу РегистрСведений.ДополнительныеСведения, запрос начинает возвращать по одной строке на каждое свойство объекта. Если у документа заполнено три доп. сведения, в выборке будет три строки с одной и той же ссылкой документа. Это и вызывает ошибку дублирования ключей.
Это самый популярный и надежный метод «схлопывания» строк. Идея заключается в том, чтобы сгруппировать результат по ссылке объекта, а значения свойств распределить по колонкам с помощью конструкции ВЫБОР и агрегатной функции МАКСИМУМ. Рассмотрим пример запроса, где нам нужно вывести три свойства: "Распечатано", "Выдано на склад" и "Отдано клиенту".
Разберем по шагам создание такого запроса:
ВЫБРАТЬ
ДополнительныеСведения.Объект КАК Объект,
МАКСИМУМ(ВЫБОР
КОГДА ДополнительныеСведения.Свойство = &СвойствоРаспечатано
ТОГДА ДополнительныеСведения.Значение
КОНЕЦ) КАК Распечатано,
МАКСИМУМ(ВЫБОР
КОГДА ДополнительныеСведения.Свойство = &СвойствоВыдано
ТОГДА ДополнительныеСведения.Значение
КОНЕЦ) КАК ВыданоНаСклад,
МАКСИМУМ(ВЫБОР
КОГДА ДополнительныеСведения.Свойство = &СвойствоОтдано
ТОГДА ДополнительныеСведения.Значение
КОНЕЦ) КАК ОтданоКлиенту
ИЗ
РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения
ГДЕ
ДополнительныеСведения.Свойство В (&СписокСвойств)
СГРУППИРОВАТЬ ПО
ДополнительныеСведения.Объект
Почему это работает:
ВЫБОР проверяет конкретный вид свойства. Если текущая запись регистра соответствует нужному свойству, берется его значение, иначе — NULL.МАКСИМУМ среди всех значений (значение свойства и куча NULL) выбирает именно заполненное значение.СГРУППИРОВАТЬ ПО объединяет все записи по одному объекту в одну единственную строку.Если количество выводимых свойств невелико и заранее известно, можно использовать несколько левых соединений к одной и той же таблице регистра, но с разными условиями связи. Проанализируем этот подход:
ВЫБРАТЬ
Док.Ссылка КАК Ссылка,
ДопСведения1.Значение КАК Распечатано,
ДопСведения2.Значение КАК ВыданоНаСклад,
ДопСведения3.Значение КАК ОтданоКлиенту
ИЗ
Документ.ЗаказПокупателя КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСведения КАК ДопСведения1
ПО Док.Ссылка = ДопСведения1.Объект
И (ДопСведения1.Свойство = &СвойствоРаспечатано)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСведения КАК ДопСведения2
ПО Док.Ссылка = ДопСведения2.Объект
И (ДопСведения2.Свойство = &СвойствоВыдано)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСведения КАК ДопСведения3
ПО Док.Ссылка = ДопСведения3.Объект
И (ДопСведения3.Свойство = &СвойствоОтдано)
Этот метод часто оказывается производительнее группировки на больших таблицах, так как СУБД проще оптимизировать соединение по конкретному значению индекса (Объект + Свойство). Однако он менее удобен, если свойств много (запрос становится "громоздким").
Платформа 1С предоставляет мощный инструмент под названием "Характеристики". Если правильно настроить его в метаданных (План видов характеристик + Регистр сведений), то в динамическом списке дополнительные сведения появятся автоматически как доступные поля.
Посмотрим на алгоритм настройки:
Преимущество этого способа в том, что платформа сама формирует оптимальный запрос и гарантирует отсутствие дублей строк.
Если логика "склеивания" очень сложная (например, нужно перечислить названия свойств строкой через запятую в одной ячейке), то средствами одного только запроса это сделать невозможно. В языке запросов 1С отсутствует аналог функции GROUP_CONCAT.
В этом случае мы можем поступить следующим образом:
ПриПолученииДанныхНаСервере.ОформленияСтрок и заполняем текстовую колонку сконкатенированными данными.Рассмотрим пример кода для такого случая:
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
МассивСсылок = Строки.ПолучитьКлючи();
// Выполняем запрос к регистру по массиву ссылок
Запрос = Новый Запрос;
Запрос.Текст = "..."; // Запрос для получения данных
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Результат = Запрос.Выполнить().Выбрать();
// Заполняем данные в строках оформления
Для каждого СтрокаСписка Из Строки Цикл
// Логика поиска данных и установки текста в колонку
СтрокаСписка.Значение.Данные["ИмяКолонки"] = "СформированнаяСтрока";
КонецЦикла;
КонецПроцедуры
Важное примечание: Этот метод очень эффективен, так как обрабатываются только те строки, которые пользователь видит прямо сейчас (обычно 20-50 строк), а не вся многотысячная таблица базы данных.
Независимо от выбранного способа, помните о следующих правилах:
ЕСТЬNULL в запросах помните, что это может затруднить использование индексов СУБД. Старайтесь подставлять значения по умолчанию (например, ЛОЖЬ для булевых свойств) только там, где это необходимо для корректного отображения.ДополнительныеСведения настроены правильные индексы по полям Объект и Свойство.Проанализировав все варианты, мы видим, что для простых случаев лучше всего подходит Способ 1 (Группировка) или Способ 3 (Характеристики). Они позволяют избежать дублирования строк и корректно вывести все необходимые данные объекта в одну строку динамического списка. При необходимости можно изучить руководство по различным способам добавления полей в динамические списки.