Как в запросе 1С вывести несколько видов контактной информации контрагента в одной строке?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При построении отчетов по продажам или взаиморасчетам часто требуется вывести контактные данные контрагентов — например, адрес, телефон и адрес электронной почты. Начинающие разработчики нередко пытаются собрать данные через оператор ОБЪЕДИНИТЬ (или ОБЪЕДИНИТЬ ВСЕ) с последующей группировкой. Однако такой подход приводит к дублированию строк или некорректным результатам.

Разберем типичную проблему, выясним причину ее возникновения и рассмотрим правильные варианты построения запросов для вывода контактной информации в отдельных колонках одной строки.

Анализ типичной ошибки: почему объединение запросов не подходит

Рассмотрим, что происходит, когда для выборки различных контактов используется конструкция объединения запросов:

  1. Каждая ветка ОБЪЕДИНИТЬ формирует отдельную строку для каждого вида контактной информации, заполняя остальные поля нулями или пустыми значениями.
  2. Для объединения этих строк приходится применять группировку СГРУППИРОВАТЬ ПО.
  3. Поскольку поля контактной информации представляют собой строки неограниченной длины, платформа 1С выдает ошибку группировки, требуя явного приведения типов с помощью конструкции ВЫРАЗИТЬ(... КАК СТРОКА(N)).
  4. Даже после приведения типов строки не схлопываются в одну запись, так как в каждой строке присутствуют разные значения (в одной — адрес и нули, в другой — телефон и нули). В результате для одного контрагента все равно формируется несколько строк.

Основное решение: использование множественных левых соединений

Чтобы получить каждый вид контактной информации в отдельной колонке для одной строки данных, необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ источника данных с таблицей контактной информации столько раз, сколько различных видов контактов требуется вывести.

Проанализируем пример запроса к табличной части контактной информации справочника контрагентов:


ВЫБРАТЬ
    СпрКонтрагенты.Ссылка КАК Контрагент,
    Телефон.Представление КАК Телефон,
    Электропочта.Представление КАК Электропочта,
    Адрес.Представление КАК ЮридическийАдрес
ИЗ
    Справочник.Контрагенты КАК СпрКонтрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Телефон
        ПО Телефон.Ссылка = СпрКонтрагенты.Ссылка
        И Телефон.Вид = &ВидКИ_Телефон
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Электропочта
        ПО Электропочта.Ссылка = СпрКонтрагенты.Ссылка
        И Электропочта.Вид = &ВидКИ_Электропочта
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Адрес
        ПО Адрес.Ссылка = СпрКонтрагенты.Ссылка
        И Адрес.Вид = &ВидКИ_Адрес

В данном запросе к основной таблице справочника трижды присоединяется табличная часть КонтактнаяИнформация, и в каждом условии соединения указывается свой отбор по виду контакта.

Соединение с регистром сведений КонтактнаяИнформация в классических конфигурациях

В конфигурациях на обычных формах (например, УТ 10.3, КА 1.1, УПП 1.3) контактная информация хранится в независимом регистре сведений РегистрСведений.КонтактнаяИнформация. При соединении с ним необходимо учитывать несколько важных правил:

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

Посмотрим на практический пример запроса по продажам с присоединением регистра сведений контактной информации:


ВЫБРАТЬ
    ПродажиОбороты.Контрагент КАК Контрагент,
    ПродажиОбороты.КоличествоОборот КАК Количество,
    ПродажиОбороты.СтоимостьОборот КАК Стоимость,
    Адрес.Представление КАК Адрес,
    Почта.Представление КАК Почта,
    Телефон.Представление КАК Телефон
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &ДатаНач,
        &ДатаКон,
        Авто,
        Номенклатура В ИЕРАРХИИ (&Номенклатура)
    ) КАК ПродажиОбороты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Адрес
        ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Адрес.Объект КАК Справочник.Контрагенты)
        И (Адрес.Вид = &ВидКААдрес)
        И (Адрес.Тип = &ТипКААдрес)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Почта
        ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Почта.Объект КАК Справочник.Контрагенты)
        И (Почта.Вид = &ВидКАПочта)
        И (Почта.Тип = &ТипКАПочта)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Телефон
        ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Телефон.Объект КАК Справочник.Контрагенты)
        И (Телефон.Вид = &ВидКАТелефон)
        И (Телефон.Тип = &ТипКАТелефон)
ГДЕ
    ПродажиОбороты.КоличествоОборот >= &КоличествоОборот
УПОРЯДОЧИТЬ ПО
    Контрагент
ИТОГИ
    СУММА(Количество),
    СУММА(Стоимость)
ПО
    Контрагент ИЕРАРХИЯ
АВТОУПОРЯДОЧИВАНИЕ

Как избежать задвоения строк и сумм оборотов

Если у контрагента в базе заведено несколько телефонных номеров или адресов с одним и тем же видом (например, два основных телефона), обычное соединение приведет к размножению строк и некорректному завышению итоговых сумм и количеств.

Чтобы гарантированно избежать этой проблемы, разберем два надежных приема:

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

Альтернативные способы в современных конфигурациях (на базе БСП)

В современных типовых решениях (1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей 11, 1С:Бухгалтерия предприятия 3.0) контактная информация хранится в структурированном виде (форматы XML или JSON в реквизите ЗначенияПолей).

Для работы с такими данными рекомендуется:

  1. Использовать программный интерфейс БСП: вызывать функции общего модуля УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(...), которые возвращают готовую таблицу с разобранными представлениями контактов для массива контрагентов.
  2. Использовать механизм Системы компоновки данных (СКД): настраивать связь нескольких наборов данных, где выборка контактов вынесена в отдельный запрос или вычисляемые поля.
← На главную