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