Как посчитать количество различных значений вне запроса или при наличии полей неограниченной длины в 1С?

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

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

Способ 1. Использование пакета запросов и временных таблиц

Если проблема заключается в том, что в основном запросе присутствуют «тяжелые» поля (например, многострочные комментарии или описания), которые мешают группировке, мы можем применить стратегию разделения данных. Разберем по шагам, как это реализовать:

  1. Выделим ключевые поля (например, ссылку на документ и идентификатор) в отдельную временную таблицу.
  2. В этой временной таблице не будет полей неограниченной длины, что позволит нам использовать функции агрегации.
  3. Выполним расчет количества различных значений по временной таблице.

Проанализируем пример кода для такого случая:


// Создаем запрос с пакетом
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Регистрация.Документ КАК Документ,
|	Регистрация.Идентификатор КАК Идентификатор
|ПОМЕСТИТЬ ВТ_ДанныеДляСчета
|ИЗ
|	РегистрСведений.ИдентификаторыОбъектов КАК Регистрация;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_ДанныеДляСчета.Документ КАК Документ,
|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_ДанныеДляСчета.Идентификатор) КАК КолРазличныхИД
|ИЗ
|	ВТ_ДанныеДляСчета КАК ВТ_ДанныеДляСчета
|ГРУППИРОВАТЬ ПО
|	ВТ_ДанныеДляСчета.Документ";

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

Этот метод считается наиболее «чистым» с точки зрения производительности СУБД, так как основная нагрузка ложится на сервер базы данных, а не на память сервера 1С. Для контроля нагрузки и своевременного освобождения ресурсов в сложных пакетах можно использовать обработку для оптимизации временных таблиц в пакетных запросах.

Способ 2. Обход ограничений с помощью функции ВЫРАЗИТЬ

Если нам все же критично оставить расчет внутри одного запроса, а мешают только строки неограниченной длины, мы можем использовать приведение типов. Выясним причину ошибки: СУБД не может сравнивать строки, длина которых не определена заранее. Решим это через ВЫРАЗИТЬ. Для решения смежных задач при работе с ограничениями типов подойдёт методика сравнения строк и представлений ссылок в запросе.

Посмотрим на пример конструкции:


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

Важный момент: при использовании ВЫРАЗИТЬ данные, выходящие за рамки указанной длины (в примере — 1000 символов), будут обрезаны. Для целей группировки и подсчета количества это обычно приемлемо.

Способ 3. Программная обработка таблицы значений через метод Свернуть

Рассмотрим ситуацию, когда данные уже получены в таблицу значений (ТЗ) и нам нужно быстро посчитать уникальные вхождения вне контекста запроса. Самый простой и наглядный способ — использование метода Свернуть.

Разберем алгоритм действий:

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

Посмотрим на пример реализации:


// Предположим, ТЗ_Результат — исходная таблица с колонками "Документ" и "ИД"
ТЗ_Счетчик = ТЗ_Результат.Скопировать();
ТЗ_Счетчик.Колонки.Добавить("Счетчик", Новый ОписаниеТипов("Число"));
ТЗ_Счетчик.ЗаполнитьЗначения(1, "Счетчик");

// Сворачиваем по нужным измерениям. В колонке "Счетчик" будет сумма вхождений, 
// а количество строк в таблице после свертки будет равно количеству РАЗЛИЧНЫХ комбинаций.
ТЗ_Счетчик.Свернуть("Документ, ИД", "Счетчик");

// Теперь, если нужно посчитать именно количество различных ИД для каждого документа:
ТЗ_Итог = ТЗ_Счетчик.Скопировать();
ТЗ_Итог.Свернуть("Документ", "Счетчик"); // Поле "Счетчик" теперь содержит количество уникальных ИД

Способ 4. Использование объекта Соответствие (Map) для высокой производительности

Если таблица значений содержит сотни тысяч строк, метод Свернуть или поиск по таблице может работать медленно. В таких случаях мы рекомендуем использовать объект Соответствие. Это один из самых быстрых способов программного поиска уникальных ключей в 1С.

Проанализируем ситуацию: нам нужно узнать общее количество различных идентификаторов во всей таблице. Объект Соответствие хранит только уникальные ключи. Если мы попытаемся вставить значение по уже существующему ключу, оно просто обновится, а количество элементов в коллекции не изменится.


УникальныеИдентификаторы = Новый Соответствие;

Для Каждого СтрокаТЗ Из ТЗ_Результат Цикл
    // Используем идентификатор как ключ. Значение может быть любым (например, Истина).
    УникальныеИдентификаторы.Вставить(СтрокаТЗ.ИД, Истина);
КонецЦикла;

КоличествоРазличных = УникальныеИдентификаторы.Количество();
Сообщить("Количество уникальных элементов: " + КоличествоРазличных);

Этот метод работает значительно быстрее, чем ТЗ.НайтиСтроки() или перебор массива с проверкой Массив.Найти(), так как поиск в Соответствии реализован через хеш-таблицы.

Способ 5. Передача внешней таблицы значений в новый запрос

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

Рассмотрим, как это сделать правильно:

  1. Убедимся, что у колонок таблицы значений жестко заданы типы (это обязательное требование для передачи ТЗ в запрос).
  2. В тексте запроса опишем структуру временной таблицы.
  3. Передадим ТЗ в параметр.

Пример кода:


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

Запрос.УстановитьПараметр("ВнешняяТЗ", МояТаблицаЗначений);
Результат = Запрос.Выполнить();

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

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

Используя эти инструменты, вы сможете эффективно обходить ограничения платформы и оптимизировать производительность ваших решений в 1С.

← На главную