В практике разработчика 1С часто возникает ситуация, когда стандартная агрегатная функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ) в запросе становится недоступной. Основной причиной обычно являются ограничения платформы: наличие в выборке полей неограниченной длины (строк или двоичных данных), использование несовместимых типов или сложная логика формирования данных, когда результат сначала попадает в таблицу значений, а уже потом требует анализа. Рассмотрим подробнее, как эффективно решить задачу подсчета уникальных идентификаторов различными способами.
Если проблема заключается в том, что в основном запросе присутствуют «тяжелые» поля (например, многострочные комментарии или описания), которые мешают группировке, мы можем применить стратегию разделения данных. Разберем по шагам, как это реализовать:
Проанализируем пример кода для такого случая:
// Создаем запрос с пакетом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Регистрация.Документ КАК Документ,
| Регистрация.Идентификатор КАК Идентификатор
|ПОМЕСТИТЬ ВТ_ДанныеДляСчета
|ИЗ
| РегистрСведений.ИдентификаторыОбъектов КАК Регистрация;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_ДанныеДляСчета.Документ КАК Документ,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_ДанныеДляСчета.Идентификатор) КАК КолРазличныхИД
|ИЗ
| ВТ_ДанныеДляСчета КАК ВТ_ДанныеДляСчета
|ГРУППИРОВАТЬ ПО
| ВТ_ДанныеДляСчета.Документ";
РезультатЗапроса = Запрос.Выполнить();
Этот метод считается наиболее «чистым» с точки зрения производительности СУБД, так как основная нагрузка ложится на сервер базы данных, а не на память сервера 1С. Для контроля нагрузки и своевременного освобождения ресурсов в сложных пакетах можно использовать обработку для оптимизации временных таблиц в пакетных запросах.
Если нам все же критично оставить расчет внутри одного запроса, а мешают только строки неограниченной длины, мы можем использовать приведение типов. Выясним причину ошибки: СУБД не может сравнивать строки, длина которых не определена заранее. Решим это через ВЫРАЗИТЬ. Для решения смежных задач при работе с ограничениями типов подойдёт методика сравнения строк и представлений ссылок в запросе.
Посмотрим на пример конструкции:
// ВЫРАЗИТЬ позволяет превратить неограниченную строку в строку фиксированной длины
Запрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(МойДокумент.Комментарий КАК СТРОКА(1000)) КАК Комментарий,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ МойДокумент.Идентификатор) КАК КоличествоУникальных
|ИЗ
| Документ.МойДокумент КАК МойДокумент
|ГРУППИРОВАТЬ ПО
| ВЫРАЗИТЬ(МойДокумент.Комментарий КАК СТРОКА(1000))";
Важный момент: при использовании ВЫРАЗИТЬ данные, выходящие за рамки указанной длины (в примере — 1000 символов), будут обрезаны. Для целей группировки и подсчета количества это обычно приемлемо.
Рассмотрим ситуацию, когда данные уже получены в таблицу значений (ТЗ) и нам нужно быстро посчитать уникальные вхождения вне контекста запроса. Самый простой и наглядный способ — использование метода Свернуть.
Разберем алгоритм действий:
Свернуть, указывая колонки, по которым ищем уникальность.Посмотрим на пример реализации:
// Предположим, ТЗ_Результат — исходная таблица с колонками "Документ" и "ИД"
ТЗ_Счетчик = ТЗ_Результат.Скопировать();
ТЗ_Счетчик.Колонки.Добавить("Счетчик", Новый ОписаниеТипов("Число"));
ТЗ_Счетчик.ЗаполнитьЗначения(1, "Счетчик");
// Сворачиваем по нужным измерениям. В колонке "Счетчик" будет сумма вхождений,
// а количество строк в таблице после свертки будет равно количеству РАЗЛИЧНЫХ комбинаций.
ТЗ_Счетчик.Свернуть("Документ, ИД", "Счетчик");
// Теперь, если нужно посчитать именно количество различных ИД для каждого документа:
ТЗ_Итог = ТЗ_Счетчик.Скопировать();
ТЗ_Итог.Свернуть("Документ", "Счетчик"); // Поле "Счетчик" теперь содержит количество уникальных ИД
Если таблица значений содержит сотни тысяч строк, метод Свернуть или поиск по таблице может работать медленно. В таких случаях мы рекомендуем использовать объект Соответствие. Это один из самых быстрых способов программного поиска уникальных ключей в 1С.
Проанализируем ситуацию: нам нужно узнать общее количество различных идентификаторов во всей таблице. Объект Соответствие хранит только уникальные ключи. Если мы попытаемся вставить значение по уже существующему ключу, оно просто обновится, а количество элементов в коллекции не изменится.
УникальныеИдентификаторы = Новый Соответствие;
Для Каждого СтрокаТЗ Из ТЗ_Результат Цикл
// Используем идентификатор как ключ. Значение может быть любым (например, Истина).
УникальныеИдентификаторы.Вставить(СтрокаТЗ.ИД, Истина);
КонецЦикла;
КоличествоРазличных = УникальныеИдентификаторы.Количество();
Сообщить("Количество уникальных элементов: " + КоличествоРазличных);
Этот метод работает значительно быстрее, чем ТЗ.НайтиСтроки() или перебор массива с проверкой Массив.Найти(), так как поиск в Соответствии реализован через хеш-таблицы.
Бывает так, что данные получены сложным путем, но вы хотите использовать мощь языка запросов для их агрегации. Платформа 1С позволяет передать таблицу значений в запрос как параметр. Это даст нам возможность снова использовать КОЛИЧЕСТВО(РАЗЛИЧНЫЕ).
Рассмотрим, как это сделать правильно:
Пример кода:
Запрос = Новый Запрос;
// Описываем временную таблицу, источником которой будет наша ТЗ
Запрос.Текст =
"ВЫБРАТЬ
| ВнешнийИсточник.Документ КАК Документ,
| ВнешнийИсточник.ИД КАК ИД
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ВнешняяТЗ КАК ВнешнийИсточник;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Данные.ИД) КАК ВсегоУникальных
|ИЗ
| ВТ_Данные КАК ВТ_Данные";
Запрос.УстановитьПараметр("ВнешняяТЗ", МояТаблицаЗначений);
Результат = Запрос.Выполнить();
Мы разобрали несколько подходов к расчету уникальных значений. Выбор конкретного метода зависит от архитектуры вашей задачи:
Используя эти инструменты, вы сможете эффективно обходить ограничения платформы и оптимизировать производительность ваших решений в 1С.