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