Как сгруппировать результат запроса по строковому полю

· 9 мин

Содержание

Чтобы собрать строки результата в группы по текстовому полю, в языке запросов 1С используют предложение СГРУППИРОВАТЬ ПО (в англоязычном варианте — GROUP BY). Строковое поле ставят в этот блок, а числовые показатели сворачивают агрегатными функциями СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Так из тысячи записей получается несколько строк — по одной на каждое уникальное значение строки.

Группировка по строке отличается от группировки по ссылке двумя вещами: текст сравнивается без учёта регистра, а строку неограниченной длины платформа вообще не даст сгруппировать без приведения через ВЫРАЗИТЬ. Разберём синтаксис, агрегаты, обработку NULL, оператор ВЫБОР и частые ошибки на рабочих примерах.

Базовый синтаксис: строка в СГРУППИРОВАТЬ ПО

Правило простое: каждое поле выборки либо стоит под агрегатной функцией, либо перечислено в СГРУППИРОВАТЬ ПО. Третьего не дано — иначе платформа вернёт ошибку ещё на этапе проверки текста запроса.

Посчитаем число позиций номенклатуры по строковому реквизиту «Бренд»:

ВЫБРАТЬ
    Номенклатура.Бренд КАК Бренд,
    КОЛИЧЕСТВО(Номенклатура.Ссылка) КАК КоличествоПозиций
ИЗ
    Справочник.Номенклатура КАК Номенклатура
СГРУППИРОВАТЬ ПО
    Номенклатура.Бренд
УПОРЯДОЧИТЬ ПО
    КоличествоПозиций УБЫВ

Что здесь происходит по шагам:

  1. Бренд — текстовый реквизит, по нему идёт группировка, поэтому он стоит в блоке СГРУППИРОВАТЬ ПО.
  2. Ссылка обёрнута в КОЛИЧЕСТВО — считаем, сколько позиций попало в каждую группу.
  3. УПОРЯДОЧИТЬ ПО применяется к уже свёрнутому результату, поэтому сортировать можно по псевдониму агрегата.

Если нужно именно число уникальных значений внутри группы, пишите КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле). Без РАЗЛИЧНЫЕ считаются все строки, включая повторы.

Строку можно и просто отобрать без агрегатов — тогда группировка работает как получение списка уникальных значений:

ВЫБРАТЬ РАЗЛИЧНЫЕ
    Контрагенты.ЮридическийАдрес КАК Адрес
ИЗ
    Справочник.Контрагенты КАК Контрагенты

ВЫБРАТЬ РАЗЛИЧНЫЕ и группировка по всем полям выборки дают один и тот же набор строк, но РАЗЛИЧНЫЕ читается понятнее, когда агрегаты не нужны.

Агрегатные функции рядом со строковым полем

Строка задаёт «разрез», числа — показатели. Типовой отчёт по продажам в разрезе текстового поля выглядит так:

ВЫБРАТЬ
    Продажи.Менеджер.Наименование КАК Менеджер,
    СУММА(Продажи.Сумма) КАК Выручка,
    СУММА(Продажи.Количество) КАК ШтукПродано,
    МАКСИМУМ(Продажи.Сумма) КАК СамаяКрупнаяПродажа
ИЗ
    РегистрНакопления.Продажи.Обороты КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Менеджер.Наименование

Под агрегат можно ставить и строку — но функций всего две: МАКСИМУМ и МИНИМУМ вернут «наибольшее» и «наименьшее» текстовое значение по алфавиту. СУММА и СРЕДНЕЕ к строкам неприменимы, платформа выдаст ошибку несовместимости типов.

Частая задача — собрать по группе одно значение строки, когда внутри группы оно одинаковое:

ВЫБРАТЬ
    Заказы.Клиент КАК Клиент,
    МАКСИМУМ(Заказы.Клиент.Телефон) КАК Телефон,
    СУММА(Заказы.Сумма) КАК СуммаЗаказов
ИЗ
    Документ.ЗаказКлиента КАК Заказы
СГРУППИРОВАТЬ ПО
    Заказы.Клиент

Телефон обёрнут в МАКСИМУМ формально — чтобы не выносить его в группировку. Приём рабочий, когда значение в группе гарантированно одно.

Пустые и NULL-значения при группировке строк

С текстом путаница возникает из-за двух разных «пусто»:

  • ❓ NULL — значения нет вовсе. Появляется при соединениях ЛЕВОЕ СОЕДИНЕНИЕ, когда справа строки не нашлось.
  • 📭 Пустая строка "" — значение есть, но оно нулевой длины. Это реальный реквизит без текста.

При группировке NULL собирается в отдельную группу, а в ячейке отчёта вместо значения будет пусто. Чтобы заменить NULL на понятный текст, используйте ЕСТЬNULL:

ВЫБРАТЬ
    ЕСТЬNULL(Остатки.Склад.Наименование, "Без склада") КАК Склад,
    СУММА(Остатки.КоличествоОстаток) КАК Остаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
СГРУППИРОВАТЬ ПО
    ЕСТЬNULL(Остатки.Склад.Наименование, "Без склада")

Выражение в СГРУППИРОВАТЬ ПО должно дословно повторять выражение из ВЫБРАТЬ. Если в выборке стоит ЕСТЬNULL(...), то и в группировке — ровно ЕСТЬNULL(...) с теми же аргументами, а не голое поле. Иначе платформа посчитает это разными выражениями.

Пустую строку "" и NULL платформа считает разными значениями, поэтому они попадут в две разные группы. Если хотите слить их в одну, приведите оба к одному виду: ЕСТЬNULL(Поле, "") заменит NULL на пустую строку, и тогда все «пустые» окажутся вместе.

Группировка по условию через оператор ВЫБОР

Когда группировать нужно не по самому значению, а по вычисленной категории, строку собирает оператор ВЫБОР … КОГДА … ТОГДА … КОНЕЦ (аналог CASE в SQL). Он возвращает текстовую метку, по которой и идёт свёртка.

Разложим продажи по ценовым сегментам:

ВЫБРАТЬ
    ВЫБОР
        КОГДА Продажи.Сумма >= 100000 ТОГДА "Крупные (от 100 тыс.)"
        КОГДА Продажи.Сумма >= 10000 ТОГДА "Средние (10–100 тыс.)"
        ИНАЧЕ "Мелкие (до 10 тыс.)"
    КОНЕЦ КАК Сегмент,
    СУММА(Продажи.Сумма) КАК Выручка,
    КОЛИЧЕСТВО(Продажи.Ссылка) КАК ЧислоСделок
ИЗ
    Документ.РеализацияТоваровУслуг КАК Продажи
СГРУППИРОВАТЬ ПО
    ВЫБОР
        КОГДА Продажи.Сумма >= 100000 ТОГДА "Крупные (от 100 тыс.)"
        КОГДА Продажи.Сумма >= 10000 ТОГДА "Средние (10–100 тыс.)"
        ИНАЧЕ "Мелкие (до 10 тыс.)"
    КОНЕЦ

Условия проверяются сверху вниз: первое сработавшее КОГДА и даёт результат, остальные не смотрятся. Поэтому порог 100000 стоит выше 10000 — если поменять местами, крупные сделки попадут в средний сегмент.

Весь блок ВЫБОР … КОНЕЦ придётся продублировать в СГРУППИРОВАТЬ ПО символ в символ. Чтобы не плодить копии, тот же результат удобнее получать во вложенном запросе или в наборе данных СКД — там выражение считается один раз.

Текстовые метки в ТОГДА лучше задавать одинаковой длины и без опечаток: две строки, различающиеся пробелом, дадут две группы.

Группировка по части строки и функции ПОДСТРОКА

Чтобы сгруппировать не по всей строке, а по её фрагменту — например, по первой букве наименования для алфавитного указателя, — применяют функцию ПОДСТРОКА(Строка, Начало, Длина). Нумерация символов начинается с единицы.

ВЫБРАТЬ
    ПОДСТРОКА(Контрагенты.Наименование, 1, 1) КАК Буква,
    КОЛИЧЕСТВО(Контрагенты.Ссылка) КАК Количество
ИЗ
    Справочник.Контрагенты КАК Контрагенты
СГРУППИРОВАТЬ ПО
    ПОДСТРОКА(Контрагенты.Наименование, 1, 1)
УПОРЯДОЧИТЬ ПО
    Буква

Так же группируют по коду региона из адреса, по префиксу артикула или по году из текстовой даты. Логика одна: ПОДСТРОКА вырезает нужный кусок, и этот же вызов повторяется в блоке группировки.

Набор строковых функций в языке запросов узкий. Доступны:

  • ✂️ ПОДСТРОКА(Строка, Начало, Длина) — вырезать фрагмент.
  • 🔗 склейка строк оператором + — например, "Регион " + Поле.
  • 🧩 ВЫРАЗИТЬ(Поле КАК СТРОКА(N)) — привести тип и ограничить длину.

Функций перевода регистра и обрезки пробелов (ВРег, СокрЛП) в запросе нет — это встроенные функции языка, а не запроса. Если текст нужно нормализовать перед группировкой, делайте это во вложенном запросе через ВЫРАЗИТЬ либо уже в коде после получения выборки.

Строки неограниченной длины: ошибка и приведение ВЫРАЗИТЬ

Самая частая проблема при группировке текста — реквизит типа «Строка» с неограниченной длиной (галка «Неограниченная» в конфигураторе). Платформа такие поля не умеет группировать, сортировать и ставить в условие РАЗЛИЧНЫЕ. Запрос упадёт с ошибкой вида «Поле не может использоваться в этом разделе» или «недопустимо использование полей неограниченной длины».

Типичный кандидат на такую ошибку — реквизит Комментарий у документов. Лечится приведением к строке фиксированной длины:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(200)) КАК Комментарий,
    КОЛИЧЕСТВО(Реализация.Ссылка) КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(200))

ВЫРАЗИТЬ(… КАК СТРОКА(200)) отрезает значение до 200 символов и делает тип ограниченным — после этого группировка разрешена. Длину берите с запасом под реальные данные: если обрезать слишком коротко, разные комментарии с одинаковым началом сольются в одну группу.

Та же техника нужна для составных (Составной тип) полей: ВЫРАЗИТЬ(Поле КАК СТРОКА(150)) оставляет только строковую часть значения.

Группировка по длинной строке тяжелее для СУБД, чем по ссылке или числу: сервер сравнивает текст целиком. Если по полю строится отчёт регулярно, подумайте о вынесении признака в отдельный короткий реквизит или ресурс регистра.

Регистр, пробелы и скорость на больших таблицах

Несколько практических моментов, которые экономят время:

  • 🔠 Регистр не учитывается. В типовом сравнении строк платформа не различает «ООО Ромашка» и «ооо ромашка» — они попадут в одну группу. В результат при этом подставится один из вариантов написания, а не оба, и заранее предсказать какой нельзя.
  • 🪪 Ведущие и хвостовые пробелы считаются. Строки "Иванов" и "Иванов " — это разные значения и разные группы. Лишние пробелы в данных — частая причина «задвоения» строк в отчёте.
  • 📐 Короткое поле группируется быстрее. Группировка по строке длиной 10 символов легче, чем по строке на 500. Для больших выборок ограничивайте длину через ВЫРАЗИТЬ.
  • 🧮 Считайте в СУБД, а не в коде. Свёртка тысяч строк запросом на сервере в разы быстрее, чем выгрузка всех записей в таблицу значений и группировка циклом.

Если результат нужно показать иерархией (например, бренд → модель), не делайте это несколькими запросами. Либо группируйте по двум полям сразу, либо используйте систему компоновки данных — в СКД группировки настраиваются без переписывания текста запроса.

Отбор строк до и после группировки: ГДЕ и ИМЕЮЩИЕ

При группировке строк у отбора два места, и их легко перепутать:

  • ГДЕ отсеивает записи до свёртки — по значениям исходных полей. Например, убрать пустые наименования или взять только нужный период.
  • ИМЕЮЩИЕ отсеивает уже готовые группы после свёртки — по значениям агрегатов. Например, оставить только тех контрагентов, у кого больше трёх заказов.

Покажем оба условия в одном запросе — список складов, где за период было движение больше чем на 100 позиций, без учёта «пустого» склада:

ВЫБРАТЬ
    Движения.Склад.Наименование КАК Склад,
    СУММА(Движения.Количество) КАК Оборот
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Склад.Наименование <> ""
СГРУППИРОВАТЬ ПО
    Движения.Склад.Наименование
ИМЕЮЩИЕ
    СУММА(Движения.Количество) > 100

Порядок частей запроса фиксированный: ГДЕ идёт до СГРУППИРОВАТЬ ПО, ИМЕЮЩИЕ — после. В ИМЕЮЩИЕ ставят именно агрегатное условие; если поставить туда обычное поле, платформа потребует вынести его в группировку.

Правило на практике: фильтр по самой строке — в ГДЕ, фильтр по счётчику или сумме внутри группы — в ИМЕЮЩИЕ. Отбор в ГДЕ ещё и быстрее, потому что сокращает данные до свёртки.

Частые ошибки и как их устранить

Диагностика: выберите симптом

Частые вопросы

Чем группировка по строке отличается от группировки по ссылке?
Технически — ничем в синтаксисе, строка так же ставится в СГРУППИРОВАТЬ ПО. Но строка сравнивается без учёта регистра и чувствительна к пробелам, а поле неограниченной длины требует приведения через ВЫРАЗИТЬ. По ссылке таких ограничений нет.
Как сгруппировать сразу по нескольким строковым полям?
Перечислите их все в блоке СГРУППИРОВАТЬ ПО через запятую. Результат свернётся по уникальным комбинациям значений — например, по паре «Регион + Город».
Почему при группировке по комментарию запрос выдаёт ошибку?
Комментарий — строка неограниченной длины, её нельзя группировать напрямую. Оберните поле в ВЫРАЗИТЬ(Комментарий КАК СТРОКА(200)) и в выборке, и в группировке.
Можно ли группировать по первым символам строки?
Да, функцией ПОДСТРОКА(Строка, 1, N), где N — число символов. Тот же вызов повторяется в СГРУППИРОВАТЬ ПО. Так делают алфавитные указатели и группировку по префиксу.
Как убрать из результата строки с пустым значением?
Добавьте отбор ГДЕ Поле <> "" до группировки либо ИМЕЮЩИЕ для условия по агрегату уже после свёртки. Для замены NULL на текст используйте ЕСТЬNULL.