В практике разработчика 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 По СтрДлина(ИсходнаяСтрока) Цикл
Символ = Сред(ИсходнаяСтрока, к, 1);
// Если символ заглавный, добавляем спецсимвол перед ним
Если Символ = ВРег(Символ) И Символ <> НРег(Символ) Тогда
Результат = Результат + "#" + Символ;
Иначе
Результат = Результат + Символ;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Записав такое значение в отдельный реквизит НаименованиеДляГруппировки, мы сможем использовать его в запросе. Поскольку строки будут физически содержать разные символы (например, "#X" и "x"), SQL-сервер успешно их разделит при группировке.
Если строковое представление, которое мы хотим сгруппировать, является наименованием какого-либо справочника, правильнее всего будет добавить в СГРУППИРОВАТЬ ПО ссылку на этот объект (Ссылка).
Выясним, почему это помогает: ссылки в 1С уникальны на уровне GUID, и для СУБД это совершенно разные значения. Даже если наименования объектов совпадают до степени смешения (с точностью до регистра), их ссылки будут различаться.
Пример структуры запроса:
ВЫБРАТЬ
СУММА(Заказ.Количество),
Соответствия.Ссылка,
Соответствия.НаименованиеВПрайсе
ИЗ
Документ.ЗаказПоставщику.Товары КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СоответствияПрайсов КАК Соответствия
ПО ...
ГДЕ ...
СГРУППИРОВАТЬ ПО
Соответствия.Ссылка,
Соответствия.НаименованиеВПрайсе
В этом случае, даже если НаименованиеВПрайсе одинаково с точки зрения SQL, наличие уникального поля Ссылка заставит систему вывести обе записи как разные строки группировки.
Если мы имеем дело с табличными частями, где нет ссылок на конкретные строки, можно использовать поле НомерСтроки. Однако будьте осторожны: группировка по номеру строки разделит все записи, даже те, которые действительно должны быть объединены. Этот метод подходит только в узких задачах, когда нужно избежать схлопывания при соединении "один ко многим".
Проблема игнорирования регистра в запросах 1С является фундаментальной особенностью работы с СУБД. Чтобы обойти это ограничение, мы рекомендуем:
Выбор метода зависит от конкретной архитектуры вашего решения и требований к производительности системы.