В процессе разработки на платформе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда необходимо получить данные документа (шапки) и одновременно рассчитать агрегатные значения (суммы, количество, вес) по его табличной части. Казалось бы, задача тривиальная, но она усложняется, если в выборку попадают поля с типом «Строка неограниченной длины». Платформа накладывает жесткие ограничения на использование таких полей в операциях группировки. Кроме того, при использовании временных таблиц нередко возникает ошибка «Неоднозначное поле», которая ставит в тупик даже опытных разработчиков.
В этой статье мы подробно разберем, почему возникают эти сложности, и проанализируем несколько эффективных способов их решения.
Прежде чем перейти к практике, давайте разберем техническую подоплеку. Поля неограниченной длины (в СУБД это типы вроде NVARCHAR(MAX) в SQL Server или TEXT в PostgreSQL) хранятся не в основной таблице данных, а в отдельных блоках. Система управления базами данных не может эффективно сравнивать эти блоки данных между собой в стандартной операции GROUP BY. Поскольку группировка требует сопоставления значений для определения уникальности, СУБД (и, как следствие, транслятор запросов 1С) выдает ошибку.
Если в вашем запросе присутствует хотя бы одно такое поле (например, Комментарий или Адрес, заданный строкой неограниченной длины), стандартная конструкция СГРУППИРОВАТЬ ПО работать не будет.
Это самый быстрый и простой способ, если вам не нужно создавать временные таблицы. Функция ВЫРАЗИТЬ позволяет привести строку неограниченной длины к строке фиксированной длины. После такого преобразования поле становится доступным для группировки.
Рассмотрим пример кода, в котором мы ограничиваем длину строки для возможности группировки:
ВЫБРАТЬ
Заявка.Ссылка КАК Ссылка,
ВЫРАЗИТЬ(Заявка.Адрес КАК СТРОКА(1000)) КАК АдресПокупателя,
СУММА(Заявка.СписокЗаявки.Вес) КАК ОбщийВес
ИЗ
Документ.Заявка КАК Заявка
ГДЕ
Заявка.Ссылка = &Ссылка
СГРУППИРОВАТЬ ПО
Заявка.Ссылка,
ВЫРАЗИТЬ(Заявка.Адрес КАК СТРОКА(1000))
Важный нюанс: Максимально допустимая длина строки для группировки в 1С составляет 1024 символа. Если исходный текст в поле длиннее, он будет обрезан. Однако для таких данных, как адрес или ФИО, этого лимита обычно достаточно.
Если данных много или структура запроса сложная, правильнее будет разделить расчет итогов и получение основных данных. Для этого используются Временные Таблицы (ВТ). Именно в этом подходе автор темы столкнулся с ошибкой конструктора запросов.
Проанализируем ситуацию: когда мы создаем временную таблицу и даем ей имя (например, Вес), а затем в секции соединения пишем ЛЕВОЕ СОЕДИНЕНИЕ Вес КАК Вес, Конструктор запросов 1С может запутаться между именем самой таблицы в базе (временном хранилище) и её псевдонимом (алиасом) в текущем запросе.
Рассмотрим правильный алгоритм решения этой задачи по шагам:
Шаг 1. Создание временной таблицы с итогами. Здесь мы группируем только по ссылке, не затрагивая проблемные строковые поля.
ВЫБРАТЬ
ЗаявкаСписокЗаявки.Ссылка КАК Ссылка,
СУММА(ЗаявкаСписокЗаявки.Вес) КАК Вес
ПОМЕСТИТЬ ВТ_ВесПоЗаявкам
ИЗ
Документ.Заявка.СписокЗаявки КАК ЗаявкаСписокЗаявки
ГДЕ
ЗаявкаСписокЗаявки.Ссылка В (&МассивСсылок)
СГРУППИРОВАТЬ ПО
ЗаявкаСписокЗаявки.Ссылка
;
Шаг 2. Основной запрос с соединением. Здесь мы присоединяем рассчитанные данные к шапке документа. Чтобы избежать ошибки «неоднозначность связи», необходимо использовать уникальные псевдонимы (алиасы).
ВЫБРАТЬ
Заявка.Адрес КАК АдресПокупателя,
Заявка.ИтоговаяСумма КАК ИтоговаяСумма,
Заявка.Телефон КАК Телефон,
Заявка.Дата КАК Дата,
Заявка.ИмяЗаказчика КАК ФИОПокупателя,
Заявка.НомерЗаявки КАК НомерЗаявки,
ЕСТЬNULL(ВесЗаявки.Вес, 0) КАК ОбщийВес
ИЗ
Документ.Заявка КАК Заявка
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВесПоЗаявкам КАК ВесЗаявки
ПО Заявка.Ссылка = ВесЗаявки.Ссылка
ГДЕ
Заявка.Ссылка В (&МассивСсылок)
Обратите внимание на конструкцию ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВесПоЗаявкам КАК ВесЗаявки. Мы дали временной таблице имя ВТ_ВесПоЗаявкам, а в самом запросе обратились к ней через алиас ВесЗаявки. Это полностью исключает конфликты имен в парсере запросов 1С.
При работе с временными таблицами важно помнить о производительности. Проанализируем два ключевых момента:
ЯЗЫК ЗАПРОСОВ
...
СГРУППИРОВАТЬ ПО
ЗаявкаСписокЗаявки.Ссылка
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
ЛЕВОГО СОЕДИНЕНИЯ, если у документа нет строк в табличной части, поле с весом примет значение NULL. В коде 1С это может привести к ошибкам при арифметических операциях. Всегда используйте функцию ЕСТЬNULL(Поле, 0), чтобы гарантированно получить число.Существует еще один изящный способ получить данные без «обрезания» строк через ВЫРАЗИТЬ и без создания временных таблиц. Это предложение ИТОГИ. Этот метод особенно полезен, если вы выводите данные в отчет или программно перебираете выборку.
ВЫБРАТЬ
Заявка.Ссылка КАК Ссылка,
Заявка.Адрес КАК Адрес,
Заявка.СписокЗаявки.Вес КАК Вес
ИЗ
Документ.Заявка КАК Заявка
ГДЕ
Заявка.Ссылка = &Ссылка
ИТОГИ
СУММА(Вес)
ПО
Ссылка
В данном случае в результате запроса вы получите иерархическую структуру. На уровне итогов (по Ссылка) будут доступны все реквизиты шапки, включая длинные строки, и при этом рассчитанная сумма веса. Группировка как таковая здесь не используется, поэтому ограничения на длину строк не действуют.
Мы выяснили ситуацию, когда стандартная группировка невозможна, и разобрали три пути решения:
ВЫРАЗИТЬ — для простых запросов, где допустимо ограничение длины строки.Неоднозначности поля.ИТОГИ — для получения иерархических данных с агрегатами без использования GROUP BY.Используя эти приемы, вы сможете строить сложные и эффективные запросы в 1С, обходя типовые ограничения платформы и обеспечивая высокую скорость работы вашей конфигурации.