Часто при разработке на платформе 1С возникает типовая, но неочевидная для начинающих программистов задача: необходимо свести данные из двух разных таблиц в одну плоскую структуру. Рассмотрим классический пример. У нас есть две исходные таблицы: в одной хранится связка Контрагент — Телефон, а в другой Контрагент — Почта. Нам нужно получить единый список, где для каждого контрагента в одной строке будет выведен и телефон, и электронный адрес.
Разберем ситуацию, с которой сталкиваются многие разработчики: при попытке соединения или объединения в результате появляются лишние строки с пустыми значениями. Выглядит это примерно так:
Проанализируем причины такого поведения системы и выясним, как правильно выстроить запрос, чтобы строки аккуратно «схлопнулись», а дублирования не происходило.
Первое, что приходит на ум разработчику — использовать левое соединение таблиц по ключевому полю. Посмотрим, почему этот способ часто дает сбой. Если в одной из присоединяемых таблиц (или в обеих сразу) на одного контрагента приходится несколько записей — например, заведено два телефонных номера и две почты, — то при соединении по ключу Контрагент база данных сформирует декартово произведение.
Вместо ожидаемой плоской таблицы мы получим дублирование строк: система умножит количество телефонов на количество адресов электронной почты и выдаст четыре строки на одного человека. Чтобы избежать этой ошибки, перед любым соединением нам необходимо подготовить данные: каждую исходную таблицу следует предварительно сгруппировать по контрагенту, оставив только актуальные и уникальные записи.
Разберем по шагам самый популярный и надежный метод решения этой задачи, который часто советуют опытные специалисты. Суть метода заключается в использовании конструкции ОБЪЕДИНИТЬ ВСЕ с последующей группировкой результата.
Шаг 1. Формируем объединение таблиц
Сначала мы пишем запрос, который объединяет две таблицы. При этом количество и порядок полей в обеих частях запроса должны совпадать. В первой части мы выбираем телефон, а на место поля с почтой ставим значение NULL. Во второй части делаем наоборот.
ВЫБРАТЬ
ТаблицаТелефонов.Контрагент КАК Контрагент,
ТаблицаТелефонов.Телефон КАК Телефон,
NULL КАК Почта
ИЗ
ТаблицаТелефонов КАК ТаблицаТелефонов
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТаблицаПочт.Контрагент,
NULL,
ТаблицаПочт.Почта
ИЗ
ТаблицаПочт КАК ТаблицаПочт
Шаг 2. Группируем результат
Полученный результат мы оборачиваем во вложенный запрос (или помещаем во временную таблицу). В итоговом запросе нам необходимо применить конструкцию СГРУППИРОВАТЬ ПО к полю Контрагент.
Чтобы пустые значения и реальные контактные данные объединились в одну строку, для текстовых полей мы применяем агрегатную функцию МАКСИМУМ(). Посмотрим, как это работает: при сравнении в запросе 1С любое заполненное строковое значение всегда будет «больше», чем NULL или пустая строка. Таким образом, функция автоматически отбросит пустоты и выберет заполненные ячейки.
ВЫБРАТЬ
ВложенныйЗапрос.Контрагент КАК Контрагент,
МАКСИМУМ(ВложенныйЗапрос.Телефон) КАК Телефон,
МАКСИМУМ(ВложенныйЗапрос.Почта) КАК Почта
ИЗ
(ВЫБРАТЬ
ТаблицаТелефонов.Контрагент КАК Контрагент,
ТаблицаТелефонов.Телефон КАК Телефон,
NULL КАК Почта
ИЗ
ТаблицаТелефонов КАК ТаблицаТелефонов
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТаблицаПочт.Контрагент,
NULL,
ТаблицаПочт.Почта
ИЗ
ТаблицаПочт КАК ТаблицаПочт) КАК ВложенныйЗапрос
СГРУППИРОВАТЬ ПО
ВложенныйЗапрос.Контрагент
Рассмотрим еще один подход — через соединение таблиц. Если вы все же решили пойти этим путем, важно понимать: левое соединение здесь не подходит. Если мы возьмем за основную (левую) таблицу список телефонов, то контрагенты, у которых есть только электронная почта, но нет телефона, просто не попадут в наш итоговый результат.
Поэтому нам необходимо использовать конструкцию ПОЛНОЕ СОЕДИНЕНИЕ. Оно гарантирует, что в выборку попадут все записи из обеих таблиц, независимо от того, есть ли для них пара.
При этом подходе возникает важный нюанс: откуда брать ключевое поле Контрагент? Если запись пришла только из правой таблицы (где лежит почта), то поле контрагента из левой таблицы будет равно NULL. Разберем, как это красиво обойти. Для этого используем функцию ЕСТЬNULL(), которая проверит первое значение, и если его нет, подставит второе.
ВЫБРАТЬ
ЕСТЬNULL(ТаблицаТелефонов.Контрагент, ТаблицаПочт.Контрагент) КАК Контрагент,
ЕСТЬNULL(ТаблицаТелефонов.Телефон, "") КАК Телефон,
ЕСТЬNULL(ТаблицаПочт.Почта, "") КАК Почта
ИЗ
ТаблицаТелефонов КАК ТаблицаТелефонов
ПОЛНОЕ СОЕДИНЕНИЕ ТаблицаПочт КАК ТаблицаПочт
ПО ТаблицаТелефонов.Контрагент = ТаблицаПочт.Контрагент
Проанализируем еще одну частую проблему при обоих методах. Когда для контрагента отсутствует телефон или почта, система 1С по умолчанию возвращает в этих полях системное значение NULL. Если результат запроса выводится в отчет или табличную часть, пользователи увидят некрасивое техническое слово «<Null>» вместо привычной пустой ячейки.
Чтобы этого избежать, возьмите за правило всегда обрабатывать поля, в которых ожидается появление отсутствующих значений. Функция ЕСТЬNULL(Выражение, Замена) поможет нам преобразовать системную пустоту в обычную пустую строку. В наших примерах выше мы уже использовали ЕСТЬNULL(ТаблицаПочт.Почта, "") для очистки итогового результата.
Выбор конкретного метода зависит от структуры вашей базы и личных предпочтений. Однако метод «ОБЪЕДИНИТЬ ВСЕ + МАКСИМУМ» считается наиболее универсальным, так как он отлично справляется даже с теми случаями, когда логика сбора данных усложняется и количество объединяемых источников увеличивается.