ТаблицаЗначений. Проверка дублей.

Функция проверки дублей строк по значениям некоторых колонок.

Потребовалось проверить таблицу значений на наличие дублей строк.

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

В случае, если параметр «КонтролируемаяКолонка» не передан, проверка осуществляется по всем колонкам.

Так же имеется параметр, позволяющий исключить колонку «НомерСтроки» из проверки — подразумевается использование при создании проверяемой таблицы значений на основе табличной части.

 

Собственно, код ниже.

Буду рад комментариям.

// функция проверяет дубли строк таблицы значений по контролируемым колонкам
// Параметры:
// ТабЧасть — таблица значений
// КонтролируемаяКолонка — строка, списокЗначений — Наименование колонок;
// ТекстВозврата — переменная, куда будет возвращено значение
// УдалятьНомерСтроки — не учитывать колонку НомерСтроки, в случае, если не передан список колонок.
// Возвращаемые значения:
// в случае отсутствия дублей — ложь
// в случае наличия дублей — истина и в параметр «ТекстВозврата» записывается строковое значение дублей

Функция ПроверкаДублей(ТабЧасть, КонтролируемаяКолонка = Неопределено, ТекстВозврата = «», УдалятьНомерСтроки = Истина) Экспорт

    ЕстьДубли = Ложь;

    Если ТипЗнч(ТабЧасть) = Тип(«ТаблицаЗначений») тогда

        Если ТипЗнч(КонтролируемаяКолонка) = Тип(«Строка») тогда
           
СтрокаСвертки = СокрЛП(КонтролируемаяКолонка);
        ИначеЕсли
ТипЗнч(КонтролируемаяКолонка) = Тип(«СписокЗначений») тогда
           
СтрокаСвертки = «»;
            Для
индекс = 0 по КонтролируемаяКолонка.Количество()-1 Цикл
                Если
Индекс > 0 тогда
                   
СтрокаСвертки = СтрокаСвертки + «, «;
                КонецЕсли;
               
СтрокаСвертки = СтрокаСвертки + СокрЛП(КонтролируемаяКолонка[Индекс]);
            КонецЦикла;
        Иначе
           
// проверяем на полные дубли
           
СтрокаСвертки = «»;
           
ПерваяКолонка = Истина;
            Для
индекс = 0 по ТабЧасть.Колонки.Количество()-1 Цикл
                Если
УдалятьНомерСтроки и СокрЛП(ТабЧасть.Колонки[Индекс].Имя) = «НомерСтроки» тогда
                    Продолжить;
                КонецЕсли;
                Если не
ПерваяКолонка тогда
                   
СтрокаСвертки = СтрокаСвертки + «, «;
                КонецЕсли;
               
СтрокаСвертки = СтрокаСвертки + СокрЛП(ТабЧасть.Колонки[Индекс].Имя);
               
ПерваяКолонка = Ложь;
            КонецЦикла;
        КонецЕсли;

        тз = ТабЧасть.Скопировать();
       
тз.Колонки.Добавить(«_КолонкаЕдиницы», Новый ОписаниеТипов(Новый КвалификаторыЧисла(1, 0, ДопустимыйЗнак.Неотрицательный)));
       
тз.ЗаполнитьЗначения(1,«_КолонкаЕдиницы»);
       
тз.Свернуть(СтрокаСвертки, «_КолонкаЕдиницы»);

        для каждого стр из тз цикл
            Если
стр._КолонкаЕдиницы > 1 Тогда
               
ТекстВозврата = ТекстВозврата + «Дублирование строк: «;
               
ЕстьДубли = Истина;
                Для
ИндексКол = 0 по тз.колонки.Количество() — 1 цикл
                    Если
тз.колонки[ИндексКол].Имя = «_КолонкаЕдиницы» тогда
                       
ТекстВозврата = ТекстВозврата + » — найдено «+стр._КолонкаЕдиницы+» стр.»+Символы.ПС;
                    Иначе
                        Если
ИндексКол > 0 тогда
                           
ТекстВозврата = ТекстВозврата + «; «;
                        КонецЕсли;
                       
ТекстВозврата = ТекстВозврата + Строка(стр[ИндексКол]);
                    КонецЕсли;
                Конеццикла;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;

    Возврат ЕстьДубли;

КонецФункции

17 Comments

  1. 13jaguar

    Было бы неплохо выдавать в результате список строк с дублями. Примерно как выдает функция НайтиСтроки(). Потом проверка резульаьта на пустоту, и если он пустой, значит дублей нет. А так идея, на мой взгляд, правильная.

    Reply
  2. razin

    Спасибо. 🙂

    На самом деле он формирует строки дублей, но в строке.

    Можно переделать, что-бы возвращался список строк. немного усложнить алгоритм.

    в блоке, где идет формирование строки для вывода добавить формирование отбора

    для каждого стр из тз цикл
    Если стр._КолонкаЕдиницы > 1 Тогда
    ТекстВозврата = ТекстВозврата + «Дублирование строк: «;
    ЕстьДубли = Истина;
    
    // Создадим отбор
    Отбор = Новый Структура;
    //отбор
    
    Для ИндексКол = 0 по тз.колонки.Количество() — 1 цикл
    Если тз.колонки[ИндексКол].Имя = «_КолонкаЕдиницы» тогда
    ТекстВозврата = ТекстВозврата + » — найдено «+стр._КолонкаЕдиницы+» стр.»+Символы.ПС;
    Иначе
    // добавим в структуру отбора значение этой колонки
    Отбор.Вставить(тз.колонки[ИндексКол].Имя, стр[ИндексКол]);
    // отбор
    
    Если ИндексКол > 0 тогда
    ТекстВозврата = ТекстВозврата + «; «;
    КонецЕсли;
    ТекстВозврата = ТекстВозврата + Строка(стр[ИндексКол]);
    КонецЕсли;
    Конеццикла;
    
    // тут у нас есть структура отбора можем ее использовать для поиска строк в первоначальной таблице
    массивДублей = ТабЧасть.НайтиСтроки(Отбор);
    // теперь в переменной массивДублей у нас лежат строки, соотретствующие задублированным ПО ОДНОМУ УСЛОВИЮ!!!
    // дальше его можно или поместить в СписокЗначений или в ТаблицуЗначений, которую передать в параметре.
    // Допустим, так:
    // в параметры функции добавляем : «СписокСтрокДублей = Новый СписокЗначений»
    // ну а тут :
    // СписокСтрокДублей.Добавить(массивДублей, стр._КолонкаЕдиницы);
    // тогда на выходе у нас будет список значений, в котором представление элемента списка — это количество
    // задублированных строк, а значение — массив СтрокТабличнойЧасти.
    // Ну, как-то так… 🙂
    
    КонецЕсли;
    КонецЦикла;

    Показать

    Reply
  3. Kov495

    А может стоит сделать запрос по таблице с группировкой по всем колонкам и суммой количества?

    Таким образом получив значения строк дублей?

    Reply
  4. razin

    Можно. только тогда надо описывать все типы в таблице 🙁 пробовал запросом — с разбегу у меня не получилось…

    типа

    Выбрать * ИЗ тз

    поместить ВременнаяТаблица

    из &Таблица как тз

    и из нее потом группировать?

    там тогда запрос лучше ручками генерить. 🙁 долго — я помучался с этим и бросил — решил на циклах сделать 🙂

    Reply
  5. mxm2

    … просто выянить наличие дублей можно оценив изменение количества строк ТЗ после сворачивания.

    Reply
  6. razin

    (5) — шикарное решение.

    если нужна просто проверка на наличие факта дублей без расшифровки — сама то!

    тогда просто выкидываем циклы проверки и сразу проверяем количество строк исходной и свернутой!

    Reply
  7. AnryMc

    (5) mxm2,

    Добавлю:

    Обычно ТЗ — это результат запроса.

    Я в запросе вставляю строку примерно такую:

    «1 КАК КоличествоДублей»

    Затем сворачиваю копию ТЗ с суммированием по «КоличествоДублей» — получаю количество дублей по каждой позиции…

    Reply
  8. mxm2

    (7) AnryMc, в запросе — вообще просто, достаточно использовать группировки с суммированим Вашего поля Сумма(КоличествоДублей), даже сворачивать после не нужно

    Reply
  9. torch

    Если условия использования функции таковы, что в большинстве случаев дублей строк нет, то целесообразно было бы вставить в самое начало функции фрагмент кода:

    ТЗ1 = Новый ТаблицаЗначений;

    ТЗ1 = ТабЧасть.Выгрузить();

    ТЗ1.Свернуть(КонтролируемаяКолонка, «»);

    Если ТЗ1.Количество() = ТабЧасть.Количество() Тогда

    Возврат Ложь;

    КонецЕсли;

    Reply
  10. razin

    (9) Да. но тогда у нас только информация о наличии дублей, а о том, что именно задублировалось — нет.

    так-то не очень гуманно пользователю сообщать — у тебя дубли — ищи сам, где 🙂

    Reply
  11. 1cprogr_nsk

    (8) mxm2,

    Если запросом по табличной части документа, то дубли не найдёт т.к. будет присутствовать колонка «НомерСтроки»

    Reply
  12. mxm2

    (11) dr.death, если цель — определить наличие дублей, зачем запрашивать номер строки? Или, если нужен номер группировать с Максимум(НомерСтроки)

    Reply
  13. 1cprogr_nsk

    (12) mxm2,

    Выбрать * по ТЧ вернёт все поля в т.ч. «НомерСтроки», ведь мы же говорим о универсальность, т.е. заранее не описаваем поля, которые хотим получить.

    Reply
  14. razin

    (13) На сколько я понял, в таком случае, если в таблице у поля есть составной тип — то его нужно описывать. Иначе запрос ругается на неизвестный тип. из-за этого и было сделано на циклах, а не на запросе — я не смог это победить. а обходить в цикле все колонки и проверять — лень 🙂

    Reply
  15. 1cprogr_nsk

    (14)

    Я имел ввиду, что если в ТЧ заранее неизвестен состав колонок, то не получить написать запрос вида:

    ВЫБРАТЬ
    МАКСИМУМ(НомерСтроки) КАК НенужноеПоле,
    1 КАК КоличествоДублей,
    ПолеТЧ1, КАК Поле1,
    ПолеТЧ2, КАК Поле2,
    …
    ПолеТЧN КАК ПолеN

    Вместо этого запроса можно написать Выбрать *, но тогда не получится выполнить функцию МАКСИМУМ.

    Хотя есть вариант построить запрос в цикле

     //Проверяем, если ТИП табличная часть документа тогда
    ТекстЗапроса = «Выбрать» + Символы.ПС;
    Для каждого КолонкаТЧ Из ТабличнаяЧасть.Колонки Цикл
    ТекстЗапроса = ТекстЗапроса + ?(КолонкаТЧ.Имя = «НомерСтроки», «МАКСИМУМ(«+КолонкаТЧ.Имя+») КАК   максСтрок,», КолонкаТЧ.Имя + » КАК » + КолонкаТЧ.Имя) + Символы.ПС;
    КонецЦикла;
    ТекстЗапроса = ТекстЗапроса + «1 КАК КоличествоДублей» + Символы.ПС + «ИЗ &ТаблицаЗначений»;
    
    Reply
  16. ProProProProPro
    ТЗДубли = Новый ТаблицаЗначений;
    ТЗДубли.Колонки.Добавить(«Артикул»);
    
    Для Каждого СтрокаАртикул ИЗ ТЗОтгрузка Цикл
    Отбор = Новый Структура;
    Отбор.Вставить(«Артикул», СтрокаАртикул.Артикул);
    Строки = ТЗОтгрузка.НайтиСтроки(Отбор);
    Если Строки.Количество() > 1 Тогда
    Если ТЗДубли.Найти(СтрокаАртикул.Артикул) = Неопределено Тогда
    Для Каждого Стр ИЗ Строки Цикл
    СтрокаТЗДубли = ТЗДубли.Добавить();
    СтрокаТЗДубли.Артикул = Стр.Артикул;
    КонецЦикла;
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;
    
    Если ТЗДубли.Количество() > 0 Тогда
    Возврат ТЗДубли
    КонецЕсли;
    

    Показать

    Reply
  17. brylig

    Сделал на основе вот этого https://infostart.ru/public/95921/ по возможности универсально:

    &НаКлиенте
    Процедура НайтиДубли(Команда)
    Поля = Новый Массив;
    Поля.Добавить(«Номенклатура»);
    Поля.Добавить(«Характеристика»);
    Дубли = ПроверитьТЧНаДубли(Объект.ТЧ, Поля);
    Если Дубли.Количество() > 0 Тогда
    Для Каждого Дубль Из Дубли Цикл
    Сообщить(«Табличная часть имеет дубли в строках №:»);
    Для Каждого СтрокаДубля Из Дубль Цикл
    Сообщить(СтрокаДубля);
    КонецЦикла;
    КонецЦикла;
    КонецЕсли;
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Функция ПроверитьТЧНаДубли(Знач ТЧ, Поля) Экспорт
    Возврат ПроверитьТЗНаДубли(ТЧ.Выгрузить(), Поля);
    КонецФункции
    
    &НаСервереБезКонтекста
    //Функция поиска дублей в таблицах значений.
    //Получает:
    //ТЗ — таблицу значений (должна иметь стандартную колонку с уникальным индексом «НомерСтроки»);
    //Поля — массив полей для отбора (если нет, то используются все поля таблицы, кроме «НомерСтроки» и «ИсходныйНомерСтроки»).
    //Возвращает:
    //Массив Дубли массивов Дубль, содержащих НомерСтроки дублированных записей.
    Функция ПроверитьТЗНаДубли(ТЗ, Поля) Экспорт
    Если ТипЗнч(ТЗ) <> Тип(«ТаблицаЗначений») Тогда
    Возврат Неопределено;
    Иначе
    Отбор = Новый Структура();
    Дубли = Новый Массив;
    Если Поля.Количество() = 0 ИЛИ Поля = Неопределено Тогда
    Для Каждого Поле Из ТЗ.Колонки Цикл
    Если Поле.Имя <> «НомерСтроки» И Поле.Имя <> «ИсходныйНомерСтроки» Тогда Поля.Добавить(Поле.Имя); КонецЕсли;
    КонецЦикла;
    КонецЕсли;
    Пока ТЗ.Количество() > 0 Цикл
    СтрокаТЗ = ТЗ[0];
    Отбор.Очистить();
    Для Каждого Поле Из Поля Цикл
    КолонкаТЗ = ТЗ.Колонки.Найти(Поле);
    Если НЕ КолонкаТЗ = Неопределено Тогда Отбор.Вставить(Поле, СтрокаТЗ[Поле]); КонецЕсли;
    КонецЦикла;
    СтрокиДубля = ТЗ.НайтиСтроки(Отбор);
    Если СтрокиДубля.Количество() > 1 Тогда
    Дубль = Новый Массив;
    Для Каждого СтрокаДубля Из СтрокиДубля Цикл
    Дубль.Добавить(СтрокаДубля.НомерСтроки);
    ТЗ.Удалить(СтрокаДубля);
    КонецЦикла;
    Дубли.Добавить(Дубль);
    Иначе
    ТЗ.Удалить(СтрокиДубля[0]);
    КонецЕсли;
    КонецЦикла;
    Возврат Дубли;
    КонецЕсли;
    КонецФункции
    

    Показать

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *