В практике разработки на платформе 1С:Предприятие часто возникает задача: собрать несколько строковых значений из разных записей в одну общую строку. Если в языке SQL для этого существуют встроенные функции агрегации (например, STRING_AGG в PostgreSQL или FOR XML PATH в MS SQL Server), то язык запросов 1С традиционно не предоставляет прямого аналога для строк. Тем не менее, решение существует, и оно лежит в плоскости «спортивного» интереса и глубокого понимания механизмов соединений таблиц.
В этой статье мы подробно разберем, как можно реализовать конкатенацию строк, используя только средства языка запросов, проанализируем ограничения этого подхода и рассмотрим конкретные примеры реализации.
Представим ситуацию: у нас есть справочник Номенклатура, и для каждой позиции существует несколько кодов или характеристик, представленных отдельными строками. Нам необходимо получить результат, где одной ссылке соответствует одна строка, содержащая все коды через запятую или без разделителей.
Основная сложность заключается в том, что стандартная агрегатная функция СУММА работает только с числовыми типами данных. Попытка сложить строки напрямую в секции ВЫБРАТЬ при использовании СГРУППИРОВАТЬ ПО приведет к ошибке синтаксиса. Поэтому мы будем использовать обходные пути, основанные на соединениях таблиц.
Проанализируем самый простой и очевидный метод — последовательное присоединение строк к основной записи. Этот метод эффективен, когда мы заранее знаем максимальное количество элементов, которые нужно объединить (например, не более пяти).
Рассмотрим по шагам логику этого запроса. Мы берем основную таблицу и последовательно присоединяем к ней её же копии, используя разные условия для каждой следующей строки.
ВЫБРАТЬ
Т1.Ссылка КАК Ссылка,
Т1.Код + ЕСТЬNULL(Т2.Код, "") + ЕСТЬNULL(Т3.Код, "") КАК ОбъединеннаяСтрока
ИЗ
Справочник.Номенклатура КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
ПО Т1.Ссылка = Т2.Ссылка И Т1.Код < Т2.Код
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т3
ПО Т2.Ссылка = Т3.Ссылка И Т2.Код < Т3.Код
ГДЕ
Т1.Ссылка = &Ссылка
В данном примере мы используем условие Т1.Код < Т2.Код, чтобы обеспечить уникальность выбираемых значений в цепочке. Важно помнить: этот метод требует жесткого прописывания количества соединений в тексте запроса. Если строк окажется больше, чем соединений, часть данных будет потеряна. Если меньше — функция ЕСТЬNULL поможет избежать превращения всей строки в NULL.
Если количество строк может быть значительным (например, до 32 или 64), использование последовательных соединений сделает текст запроса нечитаемым и крайне медленным. В таких случаях мы рекомендуем использовать «бинарное дерево». Смысл метода заключается в том, чтобы соединять строки парами, затем полученные пары соединять между собой, превращая их в четверки, и так далее.
Разберем алгоритм построения такого запроса:
Этот метод позволяет за 5-6 этапов соединений склеить до 64 строк, что покрывает большинство практических задач. Однако стоит учитывать, что итоговый запрос будет весьма громоздким и сложным для отладки.
Выясним причину, по которой иногда удобно использовать условные конструкции. Если набор возможных значений ограничен и известен (например, статусы «А», «Б», «В»), мы можем применить метод «плоской таблицы».
Для каждого возможного значения мы создаем отдельную колонку, а затем суммируем их (склеиваем). Посмотрим на пример:
ВЫБРАТЬ
Т.Ссылка КАК Ссылка,
МАКСИМУМ(ВЫБОР КОГДА Т.Код = "А" ТОГДА "А" ИНАЧЕ "" КОНЕЦ) +
МАКСИМУМ(ВЫБОР КОГДА Т.Код = "Б" ТОГДА "Б" ИНАЧЕ "" КОНЕЦ) КАК РезультирующаяСтрока
ИЗ
Справочник.Номенклатура КАК Т
СГРУППИРОВАТЬ ПО
Т.Ссылка
Этот подход работает быстрее, так как не требует множественных соединений JOIN, а использует агрегацию через MAX. Но он абсолютно не пригоден, если значения кодов произвольные и заранее неизвестны.
Проанализируем техническую сторону вопроса. Платформа 1С работает с различными системами управления базами данных (СУБД): MS SQL Server, PostgreSQL, IBM DB2, Oracle. Каждая из этих систем имеет свой уникальный синтаксис для объединения строк:
string_agg(выражение, разделитель).FOR XML PATH('') или (в новых версиях) STRING_AGG.LISTAGG.Язык запросов 1С является универсальным (транслируемым). Поскольку на момент проектирования платформы единого стандарта SQL для агрегации строк не существовало, разработчики 1С не включили подобную функцию в транслятор, чтобы обеспечить идентичное поведение запросов на всех поддерживаемых СУБД.
При использовании вышеописанных методов следует учитывать несколько важных моментов:
LEFT JOIN) существенно нагружает планировщик запросов СУБД. Не рекомендуется использовать такие методы для массовой выборки данных (например, для построения отчета по всем товарам склада).СоединитьСтроки().В рамках СКД задача решается максимально элегантно: вы просто указываете выражение СоединитьСтроки(Код, ", ") в ресурсах компоновки, и платформа берет на себя всю циклическую обработку данных.
Подведем итог: объединить значения из колонки в строку только запросом возможно, если количество значений ограничено и структура запроса подготовлена заранее под это количество. Для произвольного количества строк универсального решения «в один запрос» без использования динамического формирования текста запроса на языке 1С не существует.