При написании запросов к нескольким табличным частям или справочникам разработчики нередко сталкиваются с ситуацией, когда итоговая временная таблица содержит повторяющиеся строки. Разберем типичную задачу: необходимо собрать уникальный список партнеров по заданному адресу электронной почты, выполнив поиск как среди контактной информации самих партнеров, так и среди контактной информации подчиненных контрагентов.
Проанализируем исходный текст запроса, который вызвал появление одинаковых записей:
ВЫБРАТЬ
ПартнерыКонтактнаяИнформация.Ссылка
ПОМЕСТИТЬ Партнеры
ИЗ
Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
ГДЕ
ПартнерыКонтактнаяИнформация.АдресЭП = &АдресЭП
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
КонтрагентыКонтактнаяИнформация.Ссылка.Партнер.Ссылка
ИЗ
Справочник.Контрагенты.КонтактнаяИнформация КАК КонтрагентыКонтактнаяИнформация
ГДЕ
КонтрагентыКонтактнаяИнформация.АдресЭП = &АдресЭП
;
В результате выполнения такого запроса во временной таблице Партнеры оказываются две одинаковые ссылки. Выясним причину такого поведения и рассмотрим способы корректного устранения дублей.
Главная ошибка кроется в использовании конструкции ОБЪЕДИНИТЬ ВСЕ. В языке запросов платформы 1С:Предприятие эти операторы работают следующим образом:
UNION ALL. Она соединяет результаты двух или нескольких выборок «как есть», не выполняя проверку на уникальность строк. Если один и тот же партнер был найден в первой выборке и во второй выборке, обе строки попадают в результирующий набор.UNION. Эта конструкция выполняет неявную сортировку и фильтрацию повторяющихся строк по всем полям результирующего набора, оставляя только уникальные комбинации.Таким образом, для автоматического исключения повторяющихся партнеров достаточно убрать слово ВСЕ.
Посмотрим на исправленный вариант запроса. Заменяем составную конструкцию на стандартное объединение:
ВЫБРАТЬ
ПартнерыКонтактнаяИнформация.Ссылка КАК Партнер
ПОМЕСТИТЬ Партнеры
ИЗ
Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
ГДЕ
ПартнерыКонтактнаяИнформация.АдресЭП = &АдресЭП
ОБЪЕДИНИТЬ
ВЫБРАТЬ
КонтрагентыКонтактнаяИнформация.Ссылка.Партнер
ИЗ
Справочник.Контрагенты.КонтактнаяИнформация КАК КонтрагентыКонтактнаяИнформация
ГДЕ
КонтрагентыКонтактнаяИнформация.АдресЭП = &АдресЭП
;
Обратим внимание на избыточное разыменование: в исходном коде присутствовала конструкция КонтрагентыКонтактнаяИнформация.Ссылка.Партнер.Ссылка. Реквизит Партнер справочника Контрагенты уже сам по себе является ссылкой типа СправочникСсылка.Партнеры, поэтому повторное обращение к полю Ссылка не требуется.
В некоторых случаях у одного и того же партнера или контрагента может быть зарегистрировано несколько одинаковых адресов электронной почты (например, в разных видах контактной информации). Чтобы оптимизировать промежуточный объем данных до объединения, можно добавить ключевое слово РАЗЛИЧНЫЕ в секции ВЫБРАТЬ:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ПартнерыКонтактнаяИнформация.Ссылка КАК Партнер
ПОМЕСТИТЬ Партнеры
ИЗ
Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
ГДЕ
ПартнерыКонтактнаяИнформация.АдресЭП = &АдресЭП
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
КонтрагентыКонтактнаяИнформация.Ссылка.Партнер
ИЗ
Справочник.Контрагенты.КонтактнаяИнформация КАК КонтрагентыКонтактнаяИнформация
ГДЕ
КонтрагентыКонтактнаяИнформация.АдресЭП = &АдресЭП
;
На высоконагруженных базах данных или при обработке больших объемов записей операция ОБЪЕДИНИТЬ (SQL UNION) может выполняться медленнее, чем связка ОБЪЕДИНИТЬ ВСЕ с последующей группировкой. Оптимизатор СУБД часто строит более эффективные планы выполнения (через хеширование) для агрегатных операций.
Разберем, как реализовать этот подход в два шага через пакет запросов:
ВЫБРАТЬ
ПартнерыКонтактнаяИнформация.Ссылка КАК Партнер
ПОМЕСТИТЬ ВТ_ВсеПартнеры
ИЗ
Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
ГДЕ
ПартнерыКонтактнаяИнформация.АдресЭП = &АдресЭП
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
КонтрагентыКонтактнаяИнформация.Ссылка.Партнер
ИЗ
Справочник.Контрагенты.КонтактнаяИнформация КАК КонтрагентыКонтактнаяИнформация
ГДЕ
КонтрагентыКонтактнаяИнформация.АдресЭП = &АдресЭП
;
ВЫБРАТЬ
ВТ_ВсеПартнеры.Партнер КАК Партнер
ПОМЕСТИТЬ Партнеры
ИЗ
ВТ_ВсеПартнеры КАК ВТ_ВсеПартнеры
ГДЕ
ВТ_ВсеПартнеры.Партнер ЕСТЬ НЕ NULL
И ВТ_ВсеПартнеры.Партнер <> ЗНАЧЕНИЕ(Справочник.Партнеры.ПустаяСсылка)
СГРУППИРОВАТЬ ПО
ВТ_ВсеПартнеры.Партнер
;
При написании подобных запросов обязательно учитываем архитектурные особенности конфигураций:
КонтрагентыКонтактнаяИнформация.Ссылка.Партнер порождает неявное левое соединение (LEFT JOIN). Если контрагент не привязан к партнеру, поле вернет значение NULL. Рекомендуется отсекать пустые и неопределенные значения условием ЕСТЬ НЕ NULL.КонтактнаяИнформация. При разработке в актуальных конфигурациях целесообразно строить запрос к этому регистру либо использовать функции программного интерфейса общего модуля УправлениеКонтактнойИнформацией.АдресЭП = &АдресЭП может не найти адреса, введенные с заглавных букв (например, на СУБД PostgreSQL с чувствительностью к регистру). Для надежности используем функцию приведения: ВРЕГ(АдресЭП) = ВРЕГ(&АдресЭП).