gifts2017

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

Опубликовал Сергей Разин (razin) в раздел Программирование - Инструментарий

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

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

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

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

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

 

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

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

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

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

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

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

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

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

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

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

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

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Андрей Кузнецов (13jaguar) 13.11.13 07:04
Было бы неплохо выдавать в результате список строк с дублями. Примерно как выдает функция НайтиСтроки(). Потом проверка резульаьта на пустоту, и если он пустой, значит дублей нет. А так идея, на мой взгляд, правильная.
2. Сергей Разин (razin) 13.11.13 07:59
Спасибо. :)
На самом деле он формирует строки дублей, но в строке.
Можно переделать, что-бы возвращался список строк. немного усложнить алгоритм.
в блоке, где идет формирование строки для вывода добавить формирование отбора

для каждого стр из тз цикл
 Если стр._КолонкаЕдиницы > 1 Тогда
  ТекстВозврата = ТекстВозврата + "Дублирование строк: ";
  ЕстьДубли = Истина;
				
  // Создадим отбор
  Отбор = Новый Структура;
  //отбор
				
  Для ИндексКол = 0 по тз.колонки.Количество() - 1 цикл
     Если тз.колонки[ИндексКол].Имя = "_КолонкаЕдиницы" тогда
        ТекстВозврата = ТекстВозврата + " - найдено "+стр._КолонкаЕдиницы+" стр."+Символы.ПС;
     Иначе
	// добавим в структуру отбора значение этой колонки
	Отбор.Вставить(тз.колонки[ИндексКол].Имя, стр[ИндексКол]);
	// отбор
						
	Если ИндексКол > 0 тогда
          ТекстВозврата = ТекстВозврата + "; ";
        КонецЕсли;
        ТекстВозврата = ТекстВозврата + Строка(стр[ИндексКол]);
      КонецЕсли;
  Конеццикла;
			
// тут у нас есть структура отбора можем ее использовать для поиска строк в первоначальной таблице
       массивДублей = ТабЧасть.НайтиСтроки(Отбор);
// теперь в переменной массивДублей у нас лежат строки, соотретствующие задублированным ПО ОДНОМУ УСЛОВИЮ!!!
// дальше его можно или поместить в СписокЗначений или в ТаблицуЗначений, которую передать в параметре.
// Допустим, так:
// в параметры функции добавляем : "СписокСтрокДублей = Новый СписокЗначений"
// ну а тут :
// СписокСтрокДублей.Добавить(массивДублей, стр._КолонкаЕдиницы);
// тогда на выходе у нас будет список значений, в котором представление элемента списка - это количество 
// задублированных строк, а значение - массив СтрокТабличнойЧасти.
// Ну, как-то так... :)
			
 КонецЕсли;
КонецЦикла;
...Показать Скрыть
3. Олег Хугаев (Kov495) 13.11.13 09:21
А может стоит сделать запрос по таблице с группировкой по всем колонкам и суммой количества?
Таким образом получив значения строк дублей?
4. Сергей Разин (razin) 13.11.13 10:12
Можно. только тогда надо описывать все типы в таблице :( пробовал запросом - с разбегу у меня не получилось...
типа

Выбрать * ИЗ тз
поместить ВременнаяТаблица
из &Таблица как тз

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

там тогда запрос лучше ручками генерить. :( долго - я помучался с этим и бросил - решил на циклах сделать :)
5. mxm2 mxm2 (mxm2) 13.11.13 10:20
... просто выянить наличие дублей можно оценив изменение количества строк ТЗ после сворачивания.
levante90; fomix; razin; +3 Ответить 2
6. Сергей Разин (razin) 13.11.13 10:48
(5) - шикарное решение.
если нужна просто проверка на наличие факта дублей без расшифровки - сама то!
тогда просто выкидываем циклы проверки и сразу проверяем количество строк исходной и свернутой!
7. anry mc (AnryMc) 13.11.13 11:01
(5) mxm2,

Добавлю:

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

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

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

Затем сворачиваю копию ТЗ с суммированием по "КоличествоДублей" - получаю количество дублей по каждой позиции...
8. mxm2 mxm2 (mxm2) 13.11.13 11:21
(7) AnryMc, в запросе - вообще просто, достаточно использовать группировки с суммированим Вашего поля Сумма(КоличествоДублей), даже сворачивать после не нужно
9. Тартаковский Геннадий (torch) 13.11.13 17:25
Если условия использования функции таковы, что в большинстве случаев дублей строк нет, то целесообразно было бы вставить в самое начало функции фрагмент кода:

ТЗ1 = Новый ТаблицаЗначений;
ТЗ1 = ТабЧасть.Выгрузить();
ТЗ1.Свернуть(КонтролируемаяКолонка, "");
Если ТЗ1.Количество() = ТабЧасть.Количество() Тогда
Возврат Ложь;
КонецЕсли;
Прикрепленные файлы:
10. Сергей Разин (razin) 14.11.13 08:31
(9) Да. но тогда у нас только информация о наличии дублей, а о том, что именно задублировалось - нет.
так-то не очень гуманно пользователю сообщать - у тебя дубли - ищи сам, где :)
11. Андрей Старченко (dr.death) 14.11.13 11:20
(8) mxm2,
Если запросом по табличной части документа, то дубли не найдёт т.к. будет присутствовать колонка "НомерСтроки"
12. mxm2 mxm2 (mxm2) 14.11.13 11:32
(11) dr.death, если цель - определить наличие дублей, зачем запрашивать номер строки? Или, если нужен номер группировать с Максимум(НомерСтроки)
13. Андрей Старченко (dr.death) 14.11.13 11:53
(12) mxm2,
Выбрать * по ТЧ вернёт все поля в т.ч. "НомерСтроки", ведь мы же говорим о универсальность, т.е. заранее не описаваем поля, которые хотим получить.
14. Сергей Разин (razin) 14.11.13 12:52
(13) На сколько я понял, в таком случае, если в таблице у поля есть составной тип - то его нужно описывать. Иначе запрос ругается на неизвестный тип. из-за этого и было сделано на циклах, а не на запросе - я не смог это победить. а обходить в цикле все колонки и проверять - лень :)
15. Андрей Старченко (dr.death) 14.11.13 13:16
(14) razin,
Я имел ввиду, что если в ТЧ заранее неизвестен состав колонок, то не получить написать запрос вида:
ВЫБРАТЬ
   МАКСИМУМ(НомерСтроки) КАК НенужноеПоле,
   1 КАК КоличествоДублей,
   ПолеТЧ1, КАК Поле1,
   ПолеТЧ2, КАК Поле2,
   ...
   ПолеТЧN КАК ПолеN
...Показать Скрыть

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

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