При построении отчетов по продажам или взаиморасчетам часто требуется вывести контактные данные контрагентов — например, адрес, телефон и адрес электронной почты. Начинающие разработчики нередко пытаются собрать данные через оператор ОБЪЕДИНИТЬ (или ОБЪЕДИНИТЬ ВСЕ) с последующей группировкой. Однако такой подход приводит к дублированию строк или некорректным результатам.
Разберем типичную проблему, выясним причину ее возникновения и рассмотрим правильные варианты построения запросов для вывода контактной информации в отдельных колонках одной строки.
Рассмотрим, что происходит, когда для выборки различных контактов используется конструкция объединения запросов:
ОБЪЕДИНИТЬ формирует отдельную строку для каждого вида контактной информации, заполняя остальные поля нулями или пустыми значениями.СГРУППИРОВАТЬ ПО.ВЫРАЗИТЬ(... КАК СТРОКА(N)).Чтобы получить каждый вид контактной информации в отдельной колонке для одной строки данных, необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ источника данных с таблицей контактной информации столько раз, сколько различных видов контактов требуется вывести.
Проанализируем пример запроса к табличной части контактной информации справочника контрагентов:
ВЫБРАТЬ
СпрКонтрагенты.Ссылка КАК Контрагент,
Телефон.Представление КАК Телефон,
Электропочта.Представление КАК Электропочта,
Адрес.Представление КАК ЮридическийАдрес
ИЗ
Справочник.Контрагенты КАК СпрКонтрагенты
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Телефон
ПО Телефон.Ссылка = СпрКонтрагенты.Ссылка
И Телефон.Вид = &ВидКИ_Телефон
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Электропочта
ПО Электропочта.Ссылка = СпрКонтрагенты.Ссылка
И Электропочта.Вид = &ВидКИ_Электропочта
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Адрес
ПО Адрес.Ссылка = СпрКонтрагенты.Ссылка
И Адрес.Вид = &ВидКИ_Адрес
В данном запросе к основной таблице справочника трижды присоединяется табличная часть КонтактнаяИнформация, и в каждом условии соединения указывается свой отбор по виду контакта.
В конфигурациях на обычных формах (например, УТ 10.3, КА 1.1, УПП 1.3) контактная информация хранится в независимом регистре сведений РегистрСведений.КонтактнаяИнформация. При соединении с ним необходимо учитывать несколько важных правил:
ВНУТРЕННЕЕ СОЕДИНЕНИЕ полностью исключит контрагента и данные по его оборотам из выборки.Объект имеет составной тип. Чтобы оптимизировать план запроса СУБД, в условии соединения следует явно указать проверку типа через ССЫЛКА или приведение через ВЫРАЗИТЬ.ГДЕ.Посмотрим на практический пример запроса по продажам с присоединением регистра сведений контактной информации:
ВЫБРАТЬ
ПродажиОбороты.Контрагент КАК Контрагент,
ПродажиОбороты.КоличествоОборот КАК Количество,
ПродажиОбороты.СтоимостьОборот КАК Стоимость,
Адрес.Представление КАК Адрес,
Почта.Представление КАК Почта,
Телефон.Представление КАК Телефон
ИЗ
РегистрНакопления.Продажи.Обороты(
&ДатаНач,
&ДатаКон,
Авто,
Номенклатура В ИЕРАРХИИ (&Номенклатура)
) КАК ПродажиОбороты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Адрес
ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Адрес.Объект КАК Справочник.Контрагенты)
И (Адрес.Вид = &ВидКААдрес)
И (Адрес.Тип = &ТипКААдрес)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Почта
ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Почта.Объект КАК Справочник.Контрагенты)
И (Почта.Вид = &ВидКАПочта)
И (Почта.Тип = &ТипКАПочта)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Телефон
ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Телефон.Объект КАК Справочник.Контрагенты)
И (Телефон.Вид = &ВидКАТелефон)
И (Телефон.Тип = &ТипКАТелефон)
ГДЕ
ПродажиОбороты.КоличествоОборот >= &КоличествоОборот
УПОРЯДОЧИТЬ ПО
Контрагент
ИТОГИ
СУММА(Количество),
СУММА(Стоимость)
ПО
Контрагент ИЕРАРХИЯ
АВТОУПОРЯДОЧИВАНИЕ
Если у контрагента в базе заведено несколько телефонных номеров или адресов с одним и тем же видом (например, два основных телефона), обычное соединение приведет к размножению строк и некорректному завышению итоговых сумм и количеств.
Чтобы гарантированно избежать этой проблемы, разберем два надежных приема:
МАКСИМУМ с группировкой:
ВЫБРАТЬ
ПродажиОбороты.Контрагент КАК Контрагент,
СУММА(ПродажиОбороты.КоличествоОборот) КАК Количество,
СУММА(ПродажиОбороты.СтоимостьОборот) КАК Стоимость,
МАКСИМУМ(ВЫРАЗИТЬ(Телефон.Представление КАК СТРОКА(100))) КАК Телефон
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, , ) КАК ПродажиОбороты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК Телефон
ПО ПродажиОбороты.Контрагент = ВЫРАЗИТЬ(Телефон.Объект КАК Справочник.Контрагенты)
И Телефон.Вид = &ВидКАТелефон
СГРУППИРОВАТЬ ПО
ПродажиОбороты.Контрагент
МАКСИМУМ позволяет выбрать одну строку контакта и безопасно свернуть результат по контрагенту.ЛЕВОЕ СОЕДИНЕНИЕ.В современных типовых решениях (1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей 11, 1С:Бухгалтерия предприятия 3.0) контактная информация хранится в структурированном виде (форматы XML или JSON в реквизите ЗначенияПолей).
Для работы с такими данными рекомендуется:
УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(...), которые возвращают готовую таблицу с разобранными представлениями контактов для массива контрагентов.