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

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

При разработке отчетов или обработок в 1С:Предприятие периодически возникает задача вывести два абсолютно разных списка данных в одной таблице рядом друг с другом. Традиционное ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ здесь не подходят, так как между данными нет логической связи (ключей связи). В этой статье мы подробно разберем, как решить эту задачу различными методами, начиная от чистого языка запросов и заканчивая инструментами системы компоновки данных (СКД).

Метод 1: Использование функции АВТОНОМЕРЗАПИСИ()

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

Важное ограничение: функция АВТОНОМЕРЗАПИСИ() работает только при выборке из временных таблиц. Поэтому нам необходимо сначала поместить наши данные во временные таблицы. Рассмотрим пример кода:


// Помещаем первый список во временную таблицу
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Товар
ПОМЕСТИТЬ ВТ_ПервыйСписок
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.ЭтоГруппа = ЛОЖЬ;

// Помещаем второй список во временную таблицу
ВЫБРАТЬ
    ФизическиеЛица.Ссылка КАК ФизЛицо
ПОМЕСТИТЬ ВТ_ВторойСписок
ИЗ
    Справочник.ФизическиеЛица КАК ФизическиеЛица;

// Пронумеруем оба списка и соединим их
ВЫБРАТЬ
    Список1.НомерСтроки КАК НомерСтроки,
    Список1.Товар КАК Товар
ПОМЕСТИТЬ ВТ_Пронумерованный1
ИЗ
    (ВЫБРАТЬ
        АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
        ВТ_ПервыйСписок.Товар КАК Товар
    ИЗ
        ВТ_ПервыйСписок КАК ВТ_ПервыйСписок) КАК Список1;

ВЫБРАТЬ
    Список2.НомерСтроки КАК НомерСтроки,
    Список2.ФизЛицо КАК ФизЛицо
ПОМЕСТИТЬ ВТ_Пронумерованный2
ИЗ
    (ВЫБРАТЬ
        АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
        ВТ_ВторойСписок.ФизЛицо КАК ФизЛицо
    ИЗ
        ВТ_ВторойСписок КАК ВТ_ВторойСписок) КАК Список2;

// Финальное соединение
ВЫБРАТЬ
    ЕСТЬNULL(Т1.Товар, "---") КАК Товар,
    ЕСТЬNULL(Т2.ФизЛицо, "---") КАК ФизЛицо
ИЗ
    ВТ_Пронумерованный1 КАК Т1
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Пронумерованный2 КАК Т2
        ПО Т1.НомерСтроки = Т2.НомерСтроки;

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

Метод 2: Эмуляция нумерации через самосоединение (для старых версий платформы)

Если ваша конфигурация работает на версии платформы ниже 8.3.20, функция АВТОНОМЕРЗАПИСИ() может быть недоступна. В таком случае мы можем проанализировать ситуацию и применить математический подход к нумерации через соединение таблицы самой с собой.

Суть метода: для каждой записи мы считаем, сколько записей "меньше или равны" текущей по какому-то уникальному полю (например, Код или Ссылка). Разберем этот алгоритм подробнее:

  1. Выбираем данные во временную таблицу.
  2. Соединяем эту таблицу с самой собой по условию Т1.Ссылка >= Т2.Ссылка.
  3. Группируем по Т1.Ссылка и считаем количество записей Т2.Ссылка. Это и будет наш порядковый номер.

Проанализируем пример кода для этого метода:


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

Этот метод более ресурсоемкий для СУБД, так как создает квадратичную нагрузку при соединении, поэтому используйте его только на сравнительно небольших наборах данных.

Метод 3: Использование Системы компоновки данных (СКД)

Если ваша задача — просто вывести отчет, то мучить язык запросов необязательно. Система компоновки данных позволяет выводить несвязанные наборы данных параллельно гораздо проще. Проанализируем, как настроить такую схему:

Шаг 1. Создайте два независимых набора данных (запроса) в схеме компоновки. Например, НаборДанныхТовары и НаборДанныхСклады.

Шаг 2. Перейдите на закладку "Настройки". Вместо привычной группировки "Детальные записи" создайте структуру типа "Таблица".

Шаг 3. В строках или колонках таблицы вы можете расположить группировки из разных наборов данных. Однако более эффективный способ — использовать "Группировку" без полей, а внутри неё создать две параллельные группировки.

Шаг 4. Чтобы списки шли строго параллельно, можно воспользоваться механизмом вложенных отчетов или созданием макета, где области данных из разных наборов расположены в одной строке макета 1С.

Метод 4: Программная обработка результатов через Таблицу Значений

Иногда проще и быстрее выполнить два коротких запроса и объединить их результат программно в коде встроенного языка. Выясним причину, почему это выгодно: такой код легче отлаживать, и он не перегружает сервер БД сложными соединениями.

Рассмотрим алгоритм действий:


// Выполняем запросы и получаем таблицы
Таблица1 = Запрос1.Выполнить().Выгрузить();
Таблица2 = Запрос2.Выполнить().Выгрузить();

// Создаем результирующую таблицу
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Товар");
Результат.Колонки.Добавить("ФизЛицо");

// Определяем максимальное количество строк
МаксСтрок = Макс(Таблица1.Количество(), Таблица2.Количество());

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

Этот метод гарантирует, что списки будут выведены "бок о бок" независимо от их длины и наличия общих полей. Мы просто переносим данные по индексу строки.

Метод 5: Вывод в два Табличных Документа

Если целью является формирование печатной формы, можно воспользоваться методом ВставитьОбласть. Мы выполняем пакет запросов, получаем два результата. Первый результат выводим обычным способом в ТабличныйДокумент1. Второй результат выводим в ТабличныйДокумент2. Затем, используя цикл, мы берем область из второго документа и вставляем её в первый документ справа от уже заполненных колонок.

Этот подход позволяет гибко управлять оформлением каждого списка в отдельности, не заботясь о том, как они стыкуются на уровне SQL-запроса.

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

← На главную