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

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

В практике разработки на платформе 1С:Предприятие часто возникает задача вывода данных из двух разных источников в одну таблицу «параллельно». Например, когда нам нужно сопоставить две выборки, не имеющие общих ключей связи, просто по порядку их следования. Главная сложность заключается в том, что в стандартном языке запросов 1С отсутствует встроенное поле НомерСтроки, а количество записей в выборках может не совпадать. Рассмотрим подробно, как решить эту задачу различными методами в зависимости от версии платформы и конкретных условий.

Метод 1: Использование оконных функций (для платформы 8.3.20 и выше)

Если вы работаете на современных версиях платформы, то наиболее элегантным и производительным решением будет использование оконных функций. Разберем этот подход по шагам. Оконная функция ПОРЯДКОВЫЙ_НОМЕР() позволяет пронумеровать строки внутри запроса «на лету».

Проанализируем алгоритм действий:

  1. Создадим первую временную таблицу с данными и добавим в нее поле индекса.
  2. Создадим вторую временную таблицу аналогичным образом.
  3. Выполним ПОЛНОЕ СОЕДИНЕНИЕ этих таблиц по полю индекса.

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


ВЫБРАТЬ
    ПерваяВыборка.Номенклатура КАК Товар,
    ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕМЛЯ (УПОРЯДОЧИТЬ ПО ПерваяВыборка.Номенклатура) КАК Индекс
ПОМЕСТИТЬ ВТ_Первая
ИЗ
    Справочник.Номенклатура КАК ПерваяВыборка
ГДЕ
    ПерваяВыборка.Ссылка В (&Массив1);

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВтораяВыборка.Контрагент КАК Партнер,
    ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕМЛЯ (УПОРЯДОЧИТЬ ПО ВтораяВыборка.Контрагент) КАК Индекс
ПОМЕСТИТЬ ВТ_Вторая
ИЗ
    Справочник.Контрагенты КАК ВтораяВыборка
ГДЕ
    ВтораяВыборка.Ссылка В (&Массив2);

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЕСТЬNULL(ВТ_Первая.Товар, "---") КАК Товар,
    ЕСТЬNULL(ВТ_Вторая.Партнер, "---") КАК Партнер
ИЗ
    ВТ_Первая КАК ВТ_Первая
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Вторая КАК ВТ_Вторая
        ПО ВТ_Первая.Индекс = ВТ_Вторая.Индекс

Важный нюанс: мы используем ПОЛНОЕ СОЕДИНЕНИЕ, так как заранее не знаем, в какой выборке строк больше. Чтобы избежать появления значений NULL в итоговой таблице, применяем функцию ЕСТЬNULL, заменяя пустоты на понятные пользователю значения или пустые строки.

Метод 2: Формирование индекса через соединение (для старых версий 8.3)

Если ваша конфигурация работает на версии платформы ниже 8.3.20, оконные функции недоступны. В этом случае мы воспользуемся классическим приемом эмуляции номера строки через левое соединение таблицы самой с собой по условию «больше или равно».

Выясним причину, почему это работает: для каждой строки мы считаем, сколько строк в этой же таблице имеют уникальный идентификатор (или код/наименование) меньше или равный текущему. Количество таких строк и будет являться порядковым номером.

Рассмотрим структуру такого запроса:


ВЫБРАТЬ
    Т1.Ссылка КАК Значение,
    КОЛИЧЕСТВО(Т2.Ссылка) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Индексированная
ИЗ
    Справочник.Номенклатура КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
        ПО Т1.Ссылка >= Т2.Ссылка
ГДЕ
    Т1.Ссылка В (&Массив)
    И Т2.Ссылка В (&Массив)
СГРУППИРОВАТЬ ПО
    Т1.Ссылка

Внимание: данный метод имеет квадратичную сложность O(n^2). На больших выборках (более 1000 строк) запрос может выполняться крайне медленно. Если данных много, лучше рассмотреть программный метод объединения.

Метод 3: Программное объединение через Таблицу Значений

Иногда проще и быстрее выполнить объединение прямо в коде на встроенном языке 1С. Этот метод особенно полезен, когда данные приходят из разных источников (например, один из запроса, другой из внешнего файла Excel).

Разберем по шагам, как это реализовать:

  1. Выгрузим результаты обоих запросов в отдельные объекты ТаблицаЗначений.
  2. Создадим третью таблицу, которая будет содержать колонки обеих выборок.
  3. Определим максимальное количество строк среди двух таблиц.
  4. Запустим цикл, в котором будем последовательно считывать данные и записывать их в общую таблицу.

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


// Предположим, Таблица1 и Таблица2 уже заполнены данными
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Товар");
Результат.Колонки.Добавить("Партнер");

МаксСтрок = Макс(Таблица1.Количество(), Таблица2.Количество());

Для Индекс = 0 По МаксСтрок - 1 Цикл
    НоваяСтрока = Результат.Добавить();
    
    // Пытаемся взять данные из первой таблицы
    Если Индекс < Таблица1.Количество() Тогда
        НоваяСтрока.Товар = Таблица1[Индекс].Товар;
    КонецЕсли;
    
    // Пытаемся взять данные из второй таблицы
    Если Индекс < Таблица2.Количество() Тогда
        НоваяСтрока.Партнер = Таблица2[Индекс].Партнер;
    КонецЕсли;
КонецЦикла;

Такой подход гарантирует, что данные будут выровнены по верхнему краю (начнутся на одном уровне), а пустые ячейки в более короткой выборке просто останутся незаполненными.

Метод 4: Использование смежных группировок в СКД

Система компоновки данных (СКД) предоставляет уникальный инструмент для решения этой задачи без написания сложного кода соединения. Мы можем использовать так называемые «смежные» группировки в настройках отчета.

Как это настроить:

  1. Добавьте в схему компоновки два разных набора данных (запроса).
  2. В настройках отчета (вкладка «Настройки») выделите корень отчета.
  3. Добавьте группировку (например, по детальным записям) для первого набора данных.
  4. Рядом (на том же уровне иерархии, а не внутрь) добавьте вторую группировку для второго набора данных.
  5. В результате при выводе 1С разместит эти таблицы друг за другом или рядом, в зависимости от настроек расположения группировок.

Однако, для того чтобы они выводились именно «строка в строку», в СКД чаще применяют связь наборов данных по искусственному полю «НомерСтроки», который вычисляется функцией ВычислитьВыражение("Количество()", , "Группировка", "Первая", "Текущая").

Сравнение методов и рекомендации

Подведем итог и выясним, какой метод выбрать в вашей ситуации:

Помните о работе с типами данных: при полном соединении в запросе всегда проверяйте NULL, иначе арифметические операции с полученными числами (например, сложение итогов) могут вернуть пустое значение вместо суммы.

← На главную