Как в запросе 1С найти и выделить повторяющиеся строки по нескольким полям?

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

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

Способ 1. Поиск дублей через временную таблицу с условием «ИМЕЮЩИЕ» и левое соединение

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

Разберем реализацию по шагам:

  1. Помещаем исходные строки во временную таблицу ВТ_ИсходныеДанные и индексируем ее по полям сопоставления.
  2. Формируем временную таблицу ВТ_Дубли, сгруппировав записи по проверяемым колонкам с отбором строк, повторяющихся более одного раза.
  3. В итоговой выборке левым соединением связываем исходные строки с таблицей дублей и с помощью конструкции ВЫБОР вычисляем признак совпадения.

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


ВЫБРАТЬ
    кфПеренос.Ссылка КАК Документ,
    кфПеренос.Номенклатура КАК Номенклатура,
    кфПеренос.Размер КАК Размер,
    кфПеренос.Расцветка КАК Расцветка
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
    Документ.кфПереносМеждуСкладами.Перенос КАК кфПеренос
ГДЕ
    кфПеренос.Ссылка.Дата >= &Дата
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Документ КАК Документ,
    ВТ.Номенклатура КАК Номенклатура,
    ВТ.Размер КАК Размер
ПОМЕСТИТЬ ВТ_Дубли
ИЗ
    ВТ_ИсходныеДанные КАК ВТ
ГДЕ
    НЕ ВТ.Номенклатура ЕСТЬ NULL
    И НЕ ВТ.Размер ЕСТЬ NULL
СГРУППИРОВАТЬ ПО
    ВТ.Документ,
    ВТ.Номенклатура,
    ВТ.Размер
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(*) > 1

ИНДЕКСИРОВАТЬ ПО
    Документ,
    Номенклатура,
    Размер
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Документ КАК Документ,
    ВТ.Номенклатура КАК Номенклатура,
    ВТ.Размер КАК Размер,
    ВТ.Расцветка КАК Расцветка,
    ВЫБОР
        КОГДА Дубли.Номенклатура ЕСТЬ NULL ТОГДА ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
        ИНАЧЕ ВТ.Номенклатура
    КОНЕЦ КАК ПолеУсловие,
    ВЫБОР
        КОГДА Дубли.Номенклатура ЕСТЬ NULL ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ КАК ЭтоДубль
ИЗ
    ВТ_ИсходныеДанные КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Дубли КАК Дубли
        ПО ВТ.Документ = Дубли.Документ
        И ВТ.Номенклатура = Дубли.Номенклатура
        И ВТ.Размер = Дубли.Размер

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

Также проанализируем обработку пустых значений: если поле Размер не заполнено, группировка СУБД посчитает все пустые строки дублями друг друга. Чтобы этого избежать, мы добавили проверку в секцию ГДЕ.

Способ 2. Отбор повторяющихся записей через подзапрос с условием «В»

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

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


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

Такой вариант короче в написании, однако на больших объемах данных левое соединение с предварительно проиндексированной временной таблицей отрабатывает значительно быстрее и предсказуемее для оптимизатора СУБД.

Способ 3. Программная обработка через объект «ТаблицаЗначений»

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

Разберем вариант со сворачиванием вспомогательной таблицы:


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

// Сворачиваем таблицу по ключевым колонкам с суммированием количества
ТаблицаДублей.Свернуть("Документ, Номенклатура, Размер", "КоличествоПовторов");

// Удаляем уникальные строки, оставляем только дубли
Индекс = ТаблицаДублей.Количество() - 1;
Пока Индекс >= 0 Цикл
    Если ТаблицаДублей[Индекс].КоличествоПовторов <= 1 Тогда
        ТаблицаДублей.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

// Индексируем вспомогательную таблицу для быстрого поиска
ТаблицаДублей.Индексы.Добавить("Документ, Номенклатура, Размер");

// Обходим исходную таблицу и маркируем дубли
Для Каждого Стр Из ТаблицаДанных Цикл
    СтруктураПоиска = Новый Структура("Документ, Номенклатура, Размер", Стр.Документ, Стр.Номенклатура, Стр.Размер);
    НайденнаяСтрока = ТаблицаДублей.НайтиСтроки(СтруктураПоиска);
    
    Если НайденнаяСтрока.Количество() > 0 Тогда
        // Строка повторяется в документе, выполняем целевое действие (например, выделение)
        Стр.ЭтоДубль = Истина;
    Иначе
        Стр.ЭтоДубль = Ложь;
    КонецЕсли;
КонецЦикла;

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

Способ 4. Настройка условного оформления в Системе компоновки данных (СКД)

Если задача заключается только в визуальном выделении полей (например, подсветка цветом ячейки Расцветка в отчете или печатной форме на СКД), модифицировать запрос не обязательно. Выясним, как это настроить в СКД:

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

Таким образом, для простых отчетов на СКД достаточно штатного механизма группировок и оформления, а для сложных алгоритмов и печатных форм оптимальным решением остается временная таблица с секцией ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1.

← На главную