Как полностью исключить дублирующиеся строки из результатов запроса в 1С?

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

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

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

Почему технически не подходит ВЫБРАТЬ РАЗЛИЧНЫЕ?

Проанализируем самую частую ошибку, которую допускают начинающие разработчики. При слове «дубли» первое, что приходит на ум — использовать оператор ВЫБРАТЬ РАЗЛИЧНЫЕ. Однако этот оператор предназначен исключительно для схлопывания одинаковых записей. Если в базе есть три строки «Яблоко», этот оператор оставит ровно одну строку «Яблоко». Исходная же задача требует полного исключения таких строк (чтобы в результате «Яблок» не осталось вообще). Поэтому данный оператор здесь абсолютно не применим. Нам нужны более сложные конструкции.

Решение 1: Использование временной таблицы и ИМЕЮЩИЕ КОЛИЧЕСТВО() = 1

Посмотрим на классический и самый производительный вариант. Логика проста: мы считаем, сколько раз встречается каждое наименование, и оставляем только те, которые встречаются ровно один раз. Рассмотрим подробнее алгоритм:

  1. Создаем временную таблицу на основе исходных данных.
  2. Группируем данные по полю, которое проверяем на уникальность.
  3. Накладываем условие на количество записей в группе с помощью секции ИМЕЮЩИЕ.
  4. При необходимости соединяем полученный результат с исходной таблицей, чтобы вытянуть остальные поля. Подобная логика отбора записей по уникальным полям позволяет перенести данные между похожими конфигурациями 1С без образования дублей.

Разберем пример кода запроса:


ВЫБРАТЬ
    ВашаТаблица.Наименование КАК Наименование
ПОМЕСТИТЬ ВТ_УникальныеЗаписи
ИЗ
    ВашаТаблица КАК ВашаТаблица
СГРУППИРОВАТЬ ПО
    ВашаТаблица.Наименование
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ВашаТаблица.Наименование) = 1;

ВЫБРАТЬ
    Исходная.Наименование,
    Исходная.ДругоеПоле,
    Исходная.Сумма
ИЗ
    ВашаТаблица КАК Исходная
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_УникальныеЗаписи КАК Уникальные
    ПО Исходная.Наименование = Уникальные.Наименование

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

Решение 2: Отсев через ЛЕВОЕ СОЕДИНЕНИЕ и проверку на NULL (Анти-джойн)

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

Проанализируем алгоритм:

  1. Формируем временную таблицу, где группируем данные с условием ИМЕЮЩИЕ КОЛИЧЕСТВО(Наименование) > 1. Таким образом, мы получаем список исключительно тех наименований, которые имеют дубли.
  2. Основную таблицу связываем с таблицей дублей через ЛЕВОЕ СОЕДИНЕНИЕ по полю Наименование.
  3. Накладываем в основной выборке условие ГДЕ ТаблицаДублей.Наименование ЕСТЬ NULL.

// Шаг 1: Ищем дубли
ВЫБРАТЬ
    ВашаТаблица.Наименование КАК Наименование
ПОМЕСТИТЬ ВТ_Дубли
ИЗ
    ВашаТаблица КАК ВашаТаблица
СГРУППИРОВАТЬ ПО
    ВашаТаблица.Наименование
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ВашаТаблица.Наименование) > 1;

// Шаг 2: Исключаем дубли из основного запроса
ВЫБРАТЬ
    Исходная.Наименование,
    Исходная.ДругоеПоле
ИЗ
    ВашаТаблица КАК Исходная
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Дубли КАК Дубли
    ПО Исходная.Наименование = Дубли.Наименование
ГДЕ
    Дубли.Наименование ЕСТЬ NULL

Те строки исходной таблицы, которые не являются дублями, останутся с пустыми значениями в присоединенных колонках временной таблицы. Условие ЕСТЬ NULL отсекает все записи, которые нашлись в таблице дублей. Важно: для ускорения работы обязательно задайте индекс для временной таблицы по полю поиска. Для своевременного освобождения ресурсов в сложных пакетах можно применить обработку для оптимизации временных таблиц в пакетных запросах.

Решение 3: Использование вложенного запроса и оператора В (НЕ В)

Разберем еще один способ, который часто обсуждается на форумах — вложенные запросы с оператором НЕ В.


ВЫБРАТЬ
    т1.Наименование,
    т1.ДругоеПоле
ИЗ
    ВашаТаблица КАК т1
ГДЕ НЕ т1.Наименование В (
    ВЫБРАТЬ 
        т.Наименование 
    ИЗ 
        ВашаТаблица КАК т 
    СГРУППИРОВАТЬ ПО 
        т.Наименование 
    ИМЕЮЩИЕ 
        КОЛИЧЕСТВО(т.Наименование) > 1
)

Хотя этот код выглядит компактно, проанализируем ситуацию с точки зрения производительности платформы 1С. По стандартам разработки использование вложенных запросов в условиях соединения или отбора на больших объемах данных крайне не рекомендуется, так как это вызывает существенные просадки производительности СУБД. Рекомендуем использовать этот вариант только на небольших выборках данных.

Решение 4: Программная обработка через Таблицу Значений

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

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

Посмотрим на пример программного кода:


// Предположим, РезультатЗапроса выгружен в ИсходнаяТЗ
ИсходнаяТЗ = Запрос.Выполнить().Выгрузить();

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

// Сворачиваем по нашему ключевому полю
ТЗ_Счетчик.Свернуть("Наименование", "Счетчик");

// Удаляем дубли из исходной таблицы
Для Каждого СтрокаСчетчик Из ТЗ_Счетчик Цикл
    Если СтрокаСчетчик.Счетчик > 1 Тогда
        
        // Ищем все вхождения дубля в исходной ТЗ
        Отбор = Новый Структура("Наименование", СтрокаСчетчик.Наименование);
        НайденныеСтроки = ИсходнаяТЗ.НайтиСтроки(Отбор);
        
        // Удаляем все найденные строки
        Для Каждого СтрокаУдаления Из НайденныеСтроки Цикл
            ИсходнаяТЗ.Удалить(СтрокаУдаления);
        КонецЦикла;
        
    КонецЕсли;
КонецЦикла;

Используя объект ТаблицаЗначений и методы НайтиСтроки() и Удалить(), мы добиваемся нужного результата уже на клиенте или сервере приложений, снимая нагрузку с СУБД. Однако этот способ переносит вычислительную нагрузку на сервер 1С, что нужно учитывать при работе с десятками тысяч строк.

Заключение

Мы подробно разобрали несколько способов решения задачи. Если вы работаете с запросом напрямую и таблица большая, оптимальнее всего использовать Решение 1 (группировка с отбором количества = 1) или Решение 2 (Анти-джойн). Обязательно используйте индексы во временных таблицах для ключевых полей связи. А если архитектура алгоритма требует пост-обработки, смело применяйте программное сворачивание таблиц.

← На главную