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