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