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