При разработке отчетов или обработок в 1С:Предприятие периодически возникает задача вывести два абсолютно разных списка данных в одной таблице рядом друг с другом. Традиционное ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ здесь не подходят, так как между данными нет логической связи (ключей связи). В этой статье мы подробно разберем, как решить эту задачу различными методами, начиная от чистого языка запросов и заканчивая инструментами системы компоновки данных (СКД).
Самый современный и элегантный способ — это использование встроенной функции языка запросов АВТОНОМЕРЗАПИСИ(). Разберем, как это работает. Основная идея заключается в том, чтобы пронумеровать строки в каждом списке отдельно, а затем соединить их по этому порядковому номеру.
Важное ограничение: функция АВТОНОМЕРЗАПИСИ() работает только при выборке из временных таблиц. Поэтому нам необходимо сначала поместить наши данные во временные таблицы. Рассмотрим пример кода:
// Помещаем первый список во временную таблицу
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар
ПОМЕСТИТЬ ВТ_ПервыйСписок
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ;
// Помещаем второй список во временную таблицу
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК ФизЛицо
ПОМЕСТИТЬ ВТ_ВторойСписок
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица;
// Пронумеруем оба списка и соединим их
ВЫБРАТЬ
Список1.НомерСтроки КАК НомерСтроки,
Список1.Товар КАК Товар
ПОМЕСТИТЬ ВТ_Пронумерованный1
ИЗ
(ВЫБРАТЬ
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
ВТ_ПервыйСписок.Товар КАК Товар
ИЗ
ВТ_ПервыйСписок КАК ВТ_ПервыйСписок) КАК Список1;
ВЫБРАТЬ
Список2.НомерСтроки КАК НомерСтроки,
Список2.ФизЛицо КАК ФизЛицо
ПОМЕСТИТЬ ВТ_Пронумерованный2
ИЗ
(ВЫБРАТЬ
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
ВТ_ВторойСписок.ФизЛицо КАК ФизЛицо
ИЗ
ВТ_ВторойСписок КАК ВТ_ВторойСписок) КАК Список2;
// Финальное соединение
ВЫБРАТЬ
ЕСТЬNULL(Т1.Товар, "---") КАК Товар,
ЕСТЬNULL(Т2.ФизЛицо, "---") КАК ФизЛицо
ИЗ
ВТ_Пронумерованный1 КАК Т1
ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Пронумерованный2 КАК Т2
ПО Т1.НомерСтроки = Т2.НомерСтроки;
Обратите внимание, что мы используем ПОЛНОЕ СОЕДИНЕНИЕ. Это критически важно, так как количество записей в первом и втором списке может различаться. Если использовать левое соединение, мы рискуем потерять данные из второго списка, если он окажется длиннее первого.
Если ваша конфигурация работает на версии платформы ниже 8.3.20, функция АВТОНОМЕРЗАПИСИ() может быть недоступна. В таком случае мы можем проанализировать ситуацию и применить математический подход к нумерации через соединение таблицы самой с собой.
Суть метода: для каждой записи мы считаем, сколько записей "меньше или равны" текущей по какому-то уникальному полю (например, Код или Ссылка). Разберем этот алгоритм подробнее:
Т1.Ссылка >= Т2.Ссылка.Т1.Ссылка и считаем количество записей Т2.Ссылка. Это и будет наш порядковый номер.Проанализируем пример кода для этого метода:
ВЫБРАТЬ
Т1.Ссылка КАК Товар,
КОЛИЧЕСТВО(Т2.Ссылка) КАК НомерСтроки
ИЗ
Справочник.Номенклатура КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
ПО Т1.Ссылка >= Т2.Ссылка
ГДЕ
Т1.ЭтоГруппа = ЛОЖЬ
И Т2.ЭтоГруппа = ЛОЖЬ
СГРУППИРОВАТЬ ПО
Т1.Ссылка
Этот метод более ресурсоемкий для СУБД, так как создает квадратичную нагрузку при соединении, поэтому используйте его только на сравнительно небольших наборах данных.
Если ваша задача — просто вывести отчет, то мучить язык запросов необязательно. Система компоновки данных позволяет выводить несвязанные наборы данных параллельно гораздо проще. Проанализируем, как настроить такую схему:
Шаг 1. Создайте два независимых набора данных (запроса) в схеме компоновки. Например, НаборДанныхТовары и НаборДанныхСклады.
Шаг 2. Перейдите на закладку "Настройки". Вместо привычной группировки "Детальные записи" создайте структуру типа "Таблица".
Шаг 3. В строках или колонках таблицы вы можете расположить группировки из разных наборов данных. Однако более эффективный способ — использовать "Группировку" без полей, а внутри неё создать две параллельные группировки.
Шаг 4. Чтобы списки шли строго параллельно, можно воспользоваться механизмом вложенных отчетов или созданием макета, где области данных из разных наборов расположены в одной строке макета 1С.
Иногда проще и быстрее выполнить два коротких запроса и объединить их результат программно в коде встроенного языка. Выясним причину, почему это выгодно: такой код легче отлаживать, и он не перегружает сервер БД сложными соединениями.
Рассмотрим алгоритм действий:
// Выполняем запросы и получаем таблицы
Таблица1 = Запрос1.Выполнить().Выгрузить();
Таблица2 = Запрос2.Выполнить().Выгрузить();
// Создаем результирующую таблицу
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Товар");
Результат.Колонки.Добавить("ФизЛицо");
// Определяем максимальное количество строк
МаксСтрок = Макс(Таблица1.Количество(), Таблица2.Количество());
Для Счетчик = 0 По МаксСтрок - 1 Цикл
НоваяСтрока = Результат.Добавить();
// Заполняем из первой таблицы, если строка существует
Если Счетчик < Таблица1.Количество() Тогда
НоваяСтрока.Товар = Таблица1[Счетчик].Товар;
КонецЕсли;
// Заполняем из второй таблицы, если строка существует
Если Счетчик < Таблица2.Количество() Тогда
НоваяСтрока.ФизЛицо = Таблица2[Счетчик].ФизЛицо;
КонецЕсли;
КонецЦикла;
Этот метод гарантирует, что списки будут выведены "бок о бок" независимо от их длины и наличия общих полей. Мы просто переносим данные по индексу строки.
Если целью является формирование печатной формы, можно воспользоваться методом ВставитьОбласть. Мы выполняем пакет запросов, получаем два результата. Первый результат выводим обычным способом в ТабличныйДокумент1. Второй результат выводим в ТабличныйДокумент2. Затем, используя цикл, мы берем область из второго документа и вставляем её в первый документ справа от уже заполненных колонок.
Этот подход позволяет гибко управлять оформлением каждого списка в отдельности, не заботясь о том, как они стыкуются на уровне SQL-запроса.
Подведем итог: для новых версий платформы идеальным решением будет АВТОНОМЕРЗАПИСИ() во временных таблицах с последующим ПОЛНЫМ СОЕДИНЕНИЕМ. Для простых отчетов лучше использовать возможности группировок СКД, а для сложных интерфейсных решений — программное объединение таблиц значений.