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