Почему запрос 1С не различает регистр при группировке строк и как это исправить?

Программист 1С v8.3 (Управляемые формы)
← На главную

В практике разработчика 1С часто встречается ситуация, когда строковые данные, различающиеся только регистром символов (например, "Xiaomi Mi 5X" и "Xiaomi Mi 5x"), при выполнении запроса с оператором СГРУППИРОВАТЬ ПО объединяются в одну строку. Это приводит к неверным результатам при суммировании количественных показателей или расчете итогов. Проанализируем, почему это происходит и какими способами можно добиться корректного разделения строк.

Суть проблемы и техническая причина

Рассмотрим ситуацию: у нас есть запрос, который выбирает данные из табличной части документа и соединяет их со справочником соответствий. В справочнике две разные записи имеют наименования, отличающиеся одной латинской буквой в разном регистре. В обычном списке или при сравнении в коде 1С это разные строки, но в запросе 1С они схлопываются. Разберем, почему так происходит.

Основная причина кроется в архитектуре взаимодействия платформы 1С и систем управления базами данных (СУБД), таких как MS SQL Server или PostgreSQL. При создании базы данных 1С устанавливает параметр Collation (сортировка) в значение, которое обычно включает признак _CI (Case Insensitive — нечувствительность к регистру). Поскольку запрос 1С транслируется напрямую в SQL-запрос, выполнение группировки ложится на плечи СУБД. Если СУБД настроена как регистронезависимая, она считает символы "X" и "x" идентичными.

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

Решение 1: Обработка результата в Таблице Значений

Самый простой и часто используемый способ — отказаться от группировки непосредственно в запросе и выполнить её программно. Выгрузим результат запроса в объект ТаблицаЗначений и воспользуемся штатным методом Свернуть.

Проанализируем пример кода:


// Выполняем запрос без группировки по строковому полю
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЗаказТовары.Номенклатура,
    |	ЗаказТовары.Количество КАК Количество,
    |	Соответствия.НаименованиеВПрайсе КАК Модель
    |ИЗ
    |	Документ.ЗаказПоставщику.Товары КАК ЗаказТовары
    |		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СоответствияПрайсов КАК Соответствия
    |		ПО ЗаказТовары.Номенклатура = Соответствия.Номенклатура";

РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультат = РезультатЗапроса.Выгрузить();

// Группируем данные в ТЗ, где регистр учитывается
ТаблицаРезультат.Свернуть("Номенклатура, Модель", "Количество");

Этот метод надежен, так как механизмы платформы 1С гарантированно различают "А" и "а". Однако стоит помнить, что на очень больших объемах данных (сотни тысяч строк) выгрузка и последующая свертка в памяти могут потребовать значительных ресурсов.

Решение 2: Использование хэширования или служебного поля

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

Рассмотрим вариант создания вычисляемого кода. При записи элемента справочника можно рассчитывать хэш-сумму строки или формировать специальную строку-указатель. Посмотрим на пример функции, которая преобразует строку так, чтобы регистр стал явным:


Функция ПолучитьСтрокуСРегистром(ИсходнаяСтрока)
    Результат = "";
    Для к = 1 По СтрДлина(ИсходнаяСтрока) Цикл
        Символ = Сред(ИсходнаяСтрока, к, 1);
        // Если символ заглавный, добавляем спецсимвол перед ним
        Если Символ = ВРег(Символ) И Символ <> НРег(Символ) Тогда
            Результат = Результат + "#" + Символ;
        Иначе
            Результат = Результат + Символ;
        КонецЕсли;
    КонецЦикла;
    Возврат Результат;
КонецФункции

Записав такое значение в отдельный реквизит НаименованиеДляГруппировки, мы сможем использовать его в запросе. Поскольку строки будут физически содержать разные символы (например, "#X" и "x"), SQL-сервер успешно их разделит при группировке.

Решение 3: Группировка по уникальному идентификатору (Ссылке)

Если строковое представление, которое мы хотим сгруппировать, является наименованием какого-либо справочника, правильнее всего будет добавить в СГРУППИРОВАТЬ ПО ссылку на этот объект (Ссылка).

Выясним, почему это помогает: ссылки в 1С уникальны на уровне GUID, и для СУБД это совершенно разные значения. Даже если наименования объектов совпадают до степени смешения (с точностью до регистра), их ссылки будут различаться.

Пример структуры запроса:


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

В этом случае, даже если НаименованиеВПрайсе одинаково с точки зрения SQL, наличие уникального поля Ссылка заставит систему вывести обе записи как разные строки группировки.

Решение 4: Группировка по номеру строки или идентификатору записи

Если мы имеем дело с табличными частями, где нет ссылок на конкретные строки, можно использовать поле НомерСтроки. Однако будьте осторожны: группировка по номеру строки разделит все записи, даже те, которые действительно должны быть объединены. Этот метод подходит только в узких задачах, когда нужно избежать схлопывания при соединении "один ко многим".

Подведем итоги

Проблема игнорирования регистра в запросах 1С является фундаментальной особенностью работы с СУБД. Чтобы обойти это ограничение, мы рекомендуем:

  1. Использовать свертку в Таблице Значений для небольших и средних наборов данных.
  2. Добавлять в запрос уникальные идентификаторы (Ссылки) объектов, к которым относятся строки.
  3. Внедрять дополнительные реквизиты (хэши) в справочники, если требуется высокопроизводительная группировка "грязных" строковых данных непосредственно на уровне SQL.

Выбор метода зависит от конкретной архитектуры вашего решения и требований к производительности системы.

← На главную