Как в запросе 1С объединить данные из двух таблиц с одинаковыми ключевыми полями в одну строку?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача объединения данных из двух разных источников (таблиц или временных наборов), которые имеют общие ключевые измерения. Типичная ситуация: у нас есть Таблица 1 с колонками А, Б, В и Таблица 2 с колонками А, Б, Г. Наша цель — получить результат в виде единой строки А, Б, В, Г, где поля А и Б выступают идентификаторами, а В и Г дополняют друг друга.

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

Метод 1: «Объединить и сгруппировать»

Это самый популярный и надежный способ, который работает практически в любой ситуации. Суть метода заключается в предварительном объединении всех записей в одну временную таблицу с последующей группировкой по ключевым полям.

Проанализируем алгоритм реализации:

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

Рассмотрим пример кода запроса:


ВЫБРАТЬ
    Т1.А КАК ПолеА,
    Т1.Б КАК ПолеБ,
    Т1.В КАК ПолеВ,
    NULL КАК ПолеГ
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    &Таблица1 КАК Т1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Т2.А,
    Т2.Б,
    NULL,
    Т2.Г
ИЗ
    &Таблица2 КАК Т2;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.ПолеА,
    ВТ.ПолеБ,
    МАКСИМУМ(ВТ.ПолеВ) КАК ПолеВ,
    МАКСИМУМ(ВТ.ПолеГ) КАК ПолеГ
ИЗ
    ВТ_Данные КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.ПолеА,
    ВТ.ПолеБ

Почему это работает? Функция МАКСИМУМ игнорирует значения NULL. Если для пары А и Б у нас есть одно значение в одной строке и NULL в другой, функция вернет именно значащую информацию, «схлопывая» две строки в одну.

Метод 2: Использование ПОЛНОГО СОЕДИНЕНИЯ

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

Выясним причину использования функции ЕСТЬNULL: так как при полном соединении записи могут отсутствовать в одной из сторон, нам нужно гарантировать, что ключевое поле в результате будет заполнено.


ВЫБРАТЬ
    ЕСТЬNULL(Т1.А, Т2.А) КАК ПолеА,
    ЕСТЬNULL(Т1.Б, Т2.Б) КАК ПолеБ,
    Т1.В КАК ПолеВ,
    Т2.Г КАК ПолеГ
ИЗ
    &Таблица1 КАК Т1
        ПОЛНОЕ СОЕДИНЕНИЕ &Таблица2 КАК Т2
        ПО Т1.А = Т2.А
            И Т1.Б = Т2.Б

Данный подход удобен, если вы уверены, что для каждой пары ключей в каждой таблице существует строго не более одной записи. Если же записей несколько, вы получите «картезианское произведение» (дублирование строк).

Метод 3: Сопоставление нескольких строк через номер по порядку (НПП)

Бывают сложные случаи, когда для одного набора ключей А и Б в таблицах содержится несколько разных значений В и Г. Например, в Таблице 1 есть две строки (В1, В2), и в Таблице 2 есть две строки (Г1, Г2). Обычная группировка или соединение могут выдать некорректный результат.

Разберем, как реализовать механизм «номера по порядку» внутри запроса:

  1. Для каждой таблицы создаем временную таблицу, где каждой строке присваивается порядковый номер в разрезе ключей А и Б.
  2. В языке запросов 1С это делается через соединение таблицы самой с собой по условию Т1.Ссылка >= Т2.Ссылка (или по другому уникальному полю) с последующим подсчетом количества.
  3. Затем соединяем две таблицы не только по А и Б, но и по этому служебному номеру.

В результате мы получим сопоставление: первая строка из первой таблицы соединится с первой строкой из второй, вторая — со второй и так далее. Это позволяет выводить данные параллельно в две колонки, сохраняя структуру строк.

Метод 4: Реализация в Системе компоновки данных (СКД)

Если задача стоит в рамках разработки отчета, то СКД предоставляет встроенные инструменты для решения этой проблемы без написания сложных запросов. Рассмотрим этот вариант подробнее.

Посмотрим на настройки наборов данных:

Также в СКД существует мощная функция СоединитьСтроки(). Если для одного ключа А и Б у вас много значений в колонке В, вы можете не плодить строки, а вывести их в одну ячейку через запятую. Это делается в поле «Выражение» на вкладке «Ресурсы»:

СоединитьСтроки(ВычислитьВыражениеТаблицаЗначений("В"), ", ")

Важные нюансы и оптимизация

При использовании любого из вышеперечисленных методов стоит помнить о производительности:

  1. Индексация: Если вы используете временные таблицы, обязательно создавайте индексы по полям А и Б. Это критически важно для скорости выполнения операции СГРУППИРОВАТЬ ПО или СОЕДИНЕНИЕ на больших объемах данных.
  2. Типы данных: Функция МАКСИМУМ универсальна. Она работает не только с числами, но и с датами и строками. Помните, что для строк МАКСИМУМ вернет то значение, которое стоит последним по алфавиту.
  3. Обработка NULL: Всегда проверяйте, как ваш отчет или алгоритм должен реагировать на отсутствие данных. Использование ЕСТЬNULL(Поле, 0) или ЕСТЬNULL(Поле, "") поможет избежать пустых значений в интерфейсе пользователя.

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

← На главную