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