При работе с данными в 1С часто возникает задача объединения нескольких строковых значений из разных строк Таблицы Значений в одну строку, но с учетом группировки по определенным полям. Например, у нас есть список контактов партнера, и мы хотим получить одну строку со всеми телефонами и примечаниями для каждого партнера. В этой статье мы рассмотрим несколько эффективных подходов к решению этой задачи в 1С:Предприятие 8.3.27.
Мы вместе разберем различные методы, проанализируем их особенности и выясним, какой из них лучше подходит для конкретных ситуаций. Наша цель — найти оптимальное решение, которое будет как функциональным, так и производительным.
Этот подход позволяет выполнить всю логику объединения строк непосредственно на стороне СУБД, используя возможности языка запросов 1С. Он требует создания нескольких временных таблиц для подготовки данных и определения порядка объединения.
Для начала нам понадобится исходная таблица с данными, которую мы будем группировать и конкатенировать. Мы поместим её во временную таблицу ВТ_Набор. Рассмотрим пример:
ВЫБРАТЬ
"Иван" КАК Партнер,
"89111111111" КАК Телефон,
"Олеся" КАК Примечание
ПОМЕСТИТЬ ВТ_Набор
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Петр",
"89222222222",
"Тукан"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Иван",
"89111111112",
"Снежана"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Петр",
"89222222223",
"Дятел"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Вася",
"89222222224",
"Лиса"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Вася",
"89222222225",
"Глухарь"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Вася",
"89222222226",
"Павлин"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Саша",
"89222222227",
"Дрозд"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Саша",
"89222222228",
"Лосось"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Саша",
"89222222229",
"Колбаса"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Саша",
"89222222230",
"Пиво"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Саша",
"89222222231",
"Слива"
Здесь мы создаем временную таблицу ВТ_Набор, содержащую данные о партнерах, их телефонах и примечаниях. Обратите внимание, что для одного партнера может быть несколько записей.
На следующем шаге мы объединим телефон и примечание в одну строку "Инфо" и пронумеруем каждую запись. Для этого мы используем функцию АВТОНОМЕРЗАПИСИ(), которая присваивает уникальный номер каждой строке в выборке. Важно отсортировать данные по полю группировки (Партнер) и по полю, которое будет влиять на порядок конкатенации (Телефон), чтобы обеспечить предсказуемый результат.
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТТ.Партнер КАК Партнер,
ТТ.Инфо КАК Инфо,
АВТОНОМЕРЗАПИСИ() КАК НомерЗаписи,
1 КАК Счетчик
ПОМЕСТИТЬ ВТ_НомерЗаписи
ИЗ
(ВЫБРАТЬ ПЕРВЫЕ 1000000000
Т.Партнер КАК Партнер,
Т.Телефон + " " + Т.Примечание КАК Инфо
ИЗ
ВТ_Набор КАК Т
УПОРЯДОЧИТЬ ПО
Партнер,
Т.Телефон) КАК ТТ;
Мы создали временную таблицу ВТ_НомерЗаписи, где каждая строка имеет уникальный номер в рамках общей выборки.
Теперь нам нужно определить порядковый номер каждой строки "Инфо" внутри своей группы (то есть для каждого партнера). Это делается с помощью самосоединения временной таблицы ВТ_НомерЗаписи и агрегатной функции СУММА().
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТТ.Партнер КАК Партнер,
ТТ.Инфо КАК Инфо,
СУММА(Т2.Счетчик) КАК НомерВГруппе
ПОМЕСТИТЬ ВТ_ПорядокГрупп
ИЗ
ВТ_НомерЗаписи КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_НомерЗаписи КАК Т2
ПО Т1.Партнер = Т2.Партнер
И Т1.НомерЗаписи >= Т2.НомерЗаписи
СГРУППИРОВАТЬ ПО
Т1.Партнер,
Т1.Инфо;
В результате мы получаем таблицу ВТ_ПорядокГрупп, где для каждого партнера и его "Инфо" указан порядковый номер в группе.
Наконец, мы используем агрегатную функцию МАКСИМУМ(ВЫБОР КОГДА...) для объединения строк "Инфо" в одну, используя ранее определенный НомерВГруппе. Этот метод позволяет последовательно добавлять строки, разделяя их запятой и пробелом.
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т.Партнер КАК Партнер,
МАКСИМУМ(ВЫБОР
КОГДА Т.НомерВГруппе = 1
ТОГДА Т.Инфо
ИНАЧЕ ""
КОНЕЦ) + МАКСИМУМ(ВЫБОР
КОГДА Т.НомерВГруппе = 2
ТОГДА ", " + Т.Инфо
ИНАЧЕ ""
КОНЕЦ) + МАКСИМУМ(ВЫБОР
КОГДА Т.НомерВГруппе = 3
ТОГДА ", " + Т.Инфо
ИНАЧЕ ""
КОНЕЦ) + МАКСИМУМ(ВЫБОР
КОГДА Т.НомерВГруппе = 4
ТОГДА ", " + Т.Инфо
ИНАЧЕ ""
КОНЕЦ) + МАКСИМУМ(ВЫБОР
КОГДА Т.НомерВГруппе = 5
ТОГДА ", " + Т.Инфо
ИНАЧЕ ""
КОНЕЦ) + &ДополнительныйТекстКогдаПорядокБольше5 КАК ИнфоВсе
ИЗ
ВТ_ПорядокГрупп КАК Т
СГРУППИРОВАТЬ ПО
Т.Партнер
Важный момент: если количество объединяемых строк для одного партнера может быть очень большим, жестко прописанные МАКСИМУМ(ВЫБОР КОГДА...) до 5-го элемента становятся неэффективными. В этом случае мы можем использовать параметр запроса &ДополнительныйТекстКогдаПорядокБольше5, который программно формируется в коде 1С, добавляя оставшиеся части строки. Количество частей для формирования этого параметра можно определить, например, с помощью свертки исходной таблицы по партнеру.
Плюсы этого метода: вся логика выполняется на стороне СУБД, что может быть эффективно для больших объемов данных, если СУБД хорошо оптимизирована.
Минусы: запрос становится громоздким и сложным для отладки, особенно при большом количестве конкатенируемых частей. Существуют ограничения на максимальную длину строки в запросах 1С (около 2048 символов для ограниченных строк). Если требуется дополнительная обработка или нормализация строк (например, форматирование телефонов), это будет затруднительно сделать в запросе.
Этот подход предполагает, что мы сначала получаем данные из запроса, сгруппированные по ключевым полям, а затем обрабатываем их средствами встроенного языка 1С. Такой метод обеспечивает большую гибкость и позволяет легко выполнять дополнительную обработку строк.
Сначала мы получаем все необходимые данные из базы, отсортированные по полю, по которому будет производиться группировка (например, Партнер), и по дополнительным полям, определяющим порядок конкатенации (например, Телефон, Примечание).
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Набор.Партнер КАК Партнер,
| ВТ_Набор.Телефон КАК Телефон,
| ВТ_Набор.Примечание КАК Примечание
|ИЗ
| ВТ_Набор КАК ВТ_Набор
|УПОРЯДОЧИТЬ ПО
| Партнер,
| Телефон"; // Здесь ВТ_Набор - это может быть любая таблица или регистр
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныхЗаписей = РезультатЗапроса.Выбрать();
Мы получили выборку, которую теперь будем обходить.
Далее мы обходим полученную выборку в цикле, отслеживая смену партнера. При смене партнера мы записываем накопленную строку и начинаем формировать новую. Для эффективной конкатенации строк во встроенном языке 1С рекомендуется использовать метод СтрСоединить(), передавая ему массив строк.
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Партнер");
ТаблицаРезультат.Колонки.Добавить("Контакты");
ПредыдущийПартнер = Неопределено;
МассивСтрокКонтактов = Новый Массив;
Пока ВыборкаДетальныхЗаписей.Следующий() Цикл
Если ПредыдущийПартнер = Неопределено Тогда
ПредыдущийПартнер = ВыборкаДетальныхЗаписей.Партнер;
КонецЕсли;
Если ВыборкаДетальныхЗаписей.Партнер = ПредыдущийПартнер Тогда
// Добавляем текущий контакт в массив
МассивСтрокКонтактов.Добавить(ВыборкаДетальныхЗаписей.Телефон + " " + ВыборкаДетальныхЗаписей.Примечание);
Иначе
// Партнер изменился, записываем накопленные контакты
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Партнер = ПредыдущийПартнер;
НоваяСтрока.Контакты = СтрСоединить(МассивСтрокКонтактов, ", ");
// Обнуляем массив и начинаем для нового партнера
МассивСтрокКонтактов.Очистить();
МассивСтрокКонтактов.Добавить(ВыборкаДетальныхЗаписей.Телефон + " " + ВыборкаДетальныхЗаписей.Примечание);
ПредыдущийПартнер = ВыборкаДетальныхЗаписей.Партнер;
КонецЕсли;
КонецЦикла;
// Обрабатываем последнюю группу после выхода из цикла
Если МассивСтрокКонтактов.Количество() > 0 Тогда
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Партнер = ПредыдущийПартнер;
НоваяСтрока.Контакты = СтрСоединить(МассивСтрокКонтактов, ", ");
КонецЕсли;
// Теперь ТаблицаРезультат содержит сгруппированные и объединенные контакты
// ...
Мы видим, что такой подход дает нам полный контроль над процессом. Мы можем добавлять любую логику обработки строк (нормализация, форматирование) непосредственно перед добавлением в массив. Функция СтрСоединить() принимает массив строк и разделитель, что делает ее очень удобной и производительной для этой задачи.
Плюсы этого метода: высокая гибкость в обработке строк, возможность выполнить дополнительную нормализацию или форматирование данных. Хорошая производительность при использовании СтрСоединить(), особенно для большого количества конкатенаций или длинных строк, поскольку она оптимизирована для этих задач.
Минусы: требует написания большего объема кода на встроенном языке 1С по сравнению с чисто запросным решением, если задача очень проста. Данные сначала выгружаются в оперативную память, что может быть критично при очень больших объемах данных, если не использовать выборку с обходом по группам.
Система Компоновки Данных (СКД) в 1С предоставляет мощные средства для построения отчетов, включая агрегатные функции, которые могут значительно упростить задачу конкатенации строк с группировкой. Одной из таких функций является СоединитьСтроки().
В СКД мы сначала определяем набор данных, который будет использоваться. Это может быть запрос к базе данных или ТаблицаЗначений, переданная в СКД. В нашем примере мы используем простой запрос с тестовыми данными.
ИсточникДанных1
Local
НаборДанных1
партнер
партнер
ru
Партнер
телефон
телефон
ru
Телефон
доп
доп
ru
Доп
ИсточникДанных1
ВЫБРАТЬ
"Иван" КАК партнер,
"89111111111" КАК телефон,
"Олеся" КАК доп ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Иван",
"89111111112",
"Снежана" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Петр",
"89222222222",
"Петух" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ
"Петр",
"89222222223",
"Дятел"
Мы определили поля партнер, телефон и доп. Если нам нужно объединить несколько полей в одну строку перед конкатенацией по группе, мы можем создать вычисляемое поле в СКД.
Если нам нужно объединить Телефон и Доп в одну строку, мы можем создать вычисляемое поле, например, ИнфоДляСоединения, с выражением Телефон + " " + Доп. Однако, в СКД есть более изящное решение, которое мы рассмотрим далее.
СоединитьСтроки() как ресурса.
Ключевым моментом является использование агрегатной функции СоединитьСтроки(). Эта функция может принимать в качестве параметра ТаблицаЗначений или Массив, а также символ-разделитель. Мы определим новое "итоговое поле" (ресурс) в СКД.
...
Инфо
ru
Инфо
Инфо
СоединитьСтроки(ТаблицаЗначений(Телефон, Доп), ", ", " ")
партнер
Основной
Основной
Инфо
партнер
Items
None
0001-01-01T00:00:00
0001-01-01T00:00:00
Обратите внимание на секцию <totalField>:
<expression>СоединитьСтроки(ТаблицаЗначений(Телефон, Доп), ", ", " ")</expression>
Здесь мы используем ТаблицаЗначений(Телефон, Доп), чтобы передать в функцию СоединитьСтроки() временную таблицу, состоящую из двух колонок: Телефон и Доп. Функция автоматически объединит значения этих колонок для каждой строки группы, а затем сконкатенирует полученные строки через разделитель ", ". Если бы мы хотели объединить только одно поле, мы бы передали его напрямую, например, СоединитьСтроки(Телефон, ", ").
<group>партнер</group> указывает, что агрегация должна производиться по полю партнер.
Плюсы этого метода: декларативный подход, очень удобен для формирования отчетов. Функция СоединитьСтроки() в СКД оптимизирована для этой задачи. Позволяет быстро получать сгруппированные и объединенные данные без сложного программирования.
Минусы: подходит в основном для формирования отчетов и вывода данных. Если требуется дальнейшая программная обработка этой объединенной строки, то придется получать результат СКД в ТаблицуЗначений и уже там работать с полем. Менее гибок для очень сложной логики обработки каждой подстроки перед конкатенацией.
Мы рассмотрели три основных подхода к конкатенации строк с группировкой в 1С. Чтобы сделать правильный выбор, давайте проанализируем ситуацию:
СоединитьСтроки()) будет самым простым и эффективным.В большинстве случаев для среднестатистических задач конкатенации строк с группировкой в 1С программный обход с использованием СтрСоединить() или СКД с функцией СоединитьСтроки() будут наиболее сбалансированными и производительными решениями.