В практике разработчика 1С периодически возникает специфическая задача: необходимо «приклеить» одну таблицу к другой горизонтально, при условии, что у них нет ни одного общего поля для связи (ключа). Единственным основанием для объединения является физический порядок строк — предполагается, что первая строка первой таблицы соответствует первой строке второй таблицы. В стандартном SQL для этого используются оконные функции, но в языке запросов 1С этот механизм отсутствует. Рассмотрим подробнее, как решить эту задачу различными способами, проанализируем их эффективность и возможные «подводные камни».
Если нам принципиально нужно решить задачу именно в рамках одного запроса (например, для использования в схеме компоновки данных или при работе с очень большими объемами данных, которые не хочется выгружать в память), мы можем имитировать поле «Номер строки». Разберем этот алгоритм по шагам.
Суть метода заключается в использовании внутреннего соединения таблицы самой с собой по условию «больше или равно». Для каждой строки мы подсчитываем, сколько строк в этой же таблице имеют значение ключевого поля (например, Наименование или Ссылка) меньше или равное текущему. Это количество и станет нашим порядковым номером.
Рассмотрим пример кода запроса:
ВЫБРАТЬ
КОЛИЧЕСТВО(ТаблДляНумерации.Ссылка) КАК НомерСтроки,
ИсходнаяТаблица.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК ИсходнаяТаблица
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК ТаблДляНумерации
ПО ИсходнаяТаблица.Наименование >= ТаблДляНумерации.Наименование
ГДЕ
ИсходнаяТаблица.Родитель = &Родитель
И ТаблДляНумерации.Родитель = &Родитель
СГРУППИРОВАТЬ ПО
ИсходнаяТаблица.Ссылка
УПОРЯДОЧИТЬ ПО
НомерСтроки
Важный нюанс: Проанализируем это решение. Этот метод имеет квадратичную сложность сложности O(n²). Если в вашей таблице 1000 строк, серверу придется обработать 1 000 000 сравнений. При 10 000 строк запрос может просто «зависнуть» или вызвать переполнение временных таблиц. Кроме того, поле, по которому идет сравнение (в примере — Наименование), должно быть строго уникальным. Если в таблице окажутся два одинаковых наименования, нумератор выдаст им одинаковый номер, и при последующем соединении двух таблиц произойдет «размножение» строк (картезианское произведение).
Если данные уже находятся в памяти в объекте ТаблицаЗначений, самый простой и интуитивно понятный способ — это прямой перебор индексов. Выясним, как это реализовать на практике. Допустим, у нас есть Табл1 с ФИО и Табл2 с датами рождения.
Разберем по шагам последовательность действий:
Пример реализации на языке 1С:
// Добавляем колонку нужного типа в первую таблицу
Табл1.Колонки.Добавить("ДатаРождения", Новый ОписаниеТипов("Дата"));
// Проходим циклом по индексам строк
Для сч = 0 По Табл1.Количество() - 1 Цикл
Строка1 = Табл1[сч];
Строка2 = Табл2[сч];
// Переносим данные из второй таблицы в первую
Строка1.ДатаРождения = Строка2.ДатаРождения;
КонецЦикла;
Этот метод надежен и понятен, однако при очень большом количестве строк (десятки и сотни тысяч) итератор Для каждого или обращение по индексу в цикле может работать медленнее, чем специализированные методы платформы.
Посмотрим на наиболее эффективный способ программного объединения, который является «золотым стандартом» производительности в 1С. Платформа предоставляет встроенные методы для массовой работы с массивами данных — ВыгрузитьКолонку() и ЗагрузитьКолонку().
Эти методы работают на уровне внутренней реализации платформы (на языке C++), что значительно быстрее, чем любая интерпретируемая операция в цикле. Проанализируем код:
// 1. Подготавливаем место для новых данных в приемнике
Табл1.Колонки.Добавить("ДатаРождения", Новый ОписаниеТипов("Дата"));
// 2. Выгружаем данные из источника в массив и загружаем их в приемник одним действием
МассивДанных = Табл2.ВыгрузитьКолонку("ДатаРождения");
Табл1.ЗагрузитьКолонку(МассивДанных, "ДатаРождения");
Преимущество метода: Данный подход гарантирует, что данные будут перенесены строго по индексам: 0-й элемент массива попадет в 0-ю строку таблицы, 1-й — в 1-ю и так далее. Это самый быстрый способ «склеить» две таблицы одинаковой длины по вертикали.
Иногда возникает идея использовать в запросе условие ПО ИСТИНА (Cross Join). Разберем, почему это не решит нашу задачу. Если мы соединим две таблицы по условию ИСТИНА, система создаст полное декартово произведение. Это означает, что каждая строка первой таблицы соединится с каждой строкой второй таблицы.
Если в обеих таблицах по 100 строк, в результате вы получите 10 000 строк (100 * 100). Вместо того чтобы приклеить дату к конкретному ФИО, вы получите таблицу, где у каждого человека указаны все возможные даты рождения из второй таблицы. Поэтому без уникального ключа или имитации номера строки (как в Решении 1) соединение в запросе невозможно.
Подведем итоги и выясним, какой метод использовать в зависимости от ситуации:
ТаблицуЗначений и используйте метод ЗагрузитьКолонку(). Это обеспечит максимальную скорость работы и стабильность системы.УПОРЯДОЧИТЬ ПО. В базах данных порядок строк без указания сортировки не гарантирован, и при разных запусках запроса вы можете получить «перемешанные» данные, что приведет к ошибке при склейке «по индексу».Рассмотренные инструменты позволяют гибко управлять структурой данных в 1С, даже если на уровне проектирования базы данных связи между таблицами не были предусмотрены.