gifts2017

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

Опубликовал Павел С (pavel_pss) в раздел Программирование - Практика программирования

Проверка любой таблицы на повторяющиеся строки, с выводом сообщения какие строки задублированны, и режимом "Отказ" для проведения документов.

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

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

Данную процедура лучше использовать в общем модуле и вызывать её из процедур "ПриЗаписи" или "ОбработкаПроведения", и т.п. То есть в тех, где можно сделать отказ от выполнения дальнейших дествий
В качестве Таблицы - можно указать "ТаблицуЗначений", либо "ТабличнуюЧасть" документа, Справочника и т.д.

// !!! Павел С.С. 2 июня 2011 г. 17:49:14
// Проверка любой таблицы на повторяющиеся строки
// Данную процедура лучше использовать в процедурах ПриЗаписи, ОбработкаПроведения, и т.п. Тоесть в тех, где можно сделать отказ от выполениядальнейших дествий
// Параметры:
// ТЧ - можно указать "ТаблицуЗначений", либо "ТабличнуюЧасть" документа
// Отказ - параметр "Отказ" или "Истина", "Ложь"
Процедура ПоискДублейСтрокВТабЧасти(ТЧ, Отказ) Экспорт

 Если
Тип(ТЧ) = Тип("ТаблицаЗначений") Тогда
 
ТаблЗнач = ТЧ;
 Иначе
 
ТаблЗнач = ТЧ.Выгрузить();
 КонецЕсли;

 
Отбор = Новый Структура();

 Для каждого
Стр Из ТаблЗнач Цикл
 
Отбор.Очистить();
  Для каждого
Колонки Из ТаблЗнач.Колонки Цикл
   Если
Колонки.Имя <> "НомерСтроки" Тогда
   
Отбор.Вставить(Колонки.Имя, Стр[Колонки.Имя]);
   КонецЕсли;
  КонецЦикла;

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

См. также

Подписаться Добавить вознаграждение

Комментарии

0. Павел С (pavel_pss) 01.01.70 03:00
Проверка любой таблицы на повторяющиеся строки, с выводом сообщения какие строки задублированны, и режимом "Отказ" для проведения документов.

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

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

Данную процедура лучше использовать в общем модуле и вызывать её из процедур "ПриЗаписи" или "ОбработкаПроведения", и т.п. То есть в тех, где можно сделать отказ от выполнения дальнейших дествий
В качестве Таблицы - можно указать "ТаблицуЗначений", либо "ТабличнуюЧасть" документа, Справочника и т.д.


Перейти к публикации

1. Александр Капустин (kapustinag) 25.10.11 23:29
Думаю, небрежно сделано.
1. В приведенном куске кода последняя команда "Найти" лишняя:

Если Строки.Количество() > 1 Тогда
Для каждого НайденныеСтроки Из Строки Цикл
Если Строки.Найти(НайденныеСтроки) > 0 Тогда

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

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

3. Для каждой строки ТЧ идет цикл по колонкам Отбор.Вставить... Вставить все колонки в отбор правильнее перед циклом по строкам ТЧ. А в цикле по строкам - заполнять значения отбора.

4. Отбор делается по строковому представлению, что неправильно. Есть куча примеров, когда объекты разные, но их строковое представление совпадает. Например, наименования номенклатуры совпадают, но разные артикулы.

5. Более того, зачем вообще эти многократные отборы и поиски? Лучше отсортировать таблицу значений по всем колонкам. Тогда все дублирующиеся строки окажутся рядом друг с другом. Останется только один раз пройтись циклом сверху вниз, сравнивая текущую строку со следующей.

(Если п.5 еще можно снять по той причине, что сортировка меняет исходную ТЧ - что нежелательно - то с п.п.1-4 вряд ли можно спорить)

Так что, наверно, "минус".
2. Программулькин (Программулькин) 26.10.11 05:12
на работоспособность не проверял, особо сильно не вникал, но за интересную мысль плюс, сложил в "судук", авось пригодится.
3. Вадим Никонов (V.Nikonov) 26.10.11 08:14
К тому же обработка отлавливает только АБСОЛЮТНЫХ двойников! Если например, ТЧ Товары содержит две строки с одинаковыми товарами, характеристиками, сериями и т.п., но с разным количеством по строкам, то такие строки не будут дублями....?
4. Вадим Никонов (V.Nikonov) 26.10.11 08:14
А если ТЧ документа устанавливающего свойства, и в одной строке устанавливается Истина, а вдругой Ложь? Это ошибочное состояние документа не будет распознано!!!
Сыровато, недодлано, крайне ограниченное применение идеи как таковой!
Минус пока..
5. artmicro (artmicro) 26.10.11 09:02
Для дальнейшего развития, так сказать, если твоя табличная часть будет довольно большая, не плохо было использовать индексы и исключить из проверки колонки с числовым типом.

Ну и вообщем - метод типовой. Есть куда более интересные и более продуктивные решения.
6. apalon_pss (pavel_pss) 26.10.11 09:32
Если кому не нравиться, можете не пользоваться, или пишите свои, но для начинающих программеров как пример вполне может сойти. А вы типа продвинутые программеры, если такие умные возьмите и выложите свою реализацию данного примера. А то умничать все крутые, а взять написать и выложить слабо. Так что ...
7. Иван Иванов (kosmo0) 26.10.11 13:32
(6)
Не кипи. Ты не в детском саду и здесь не будут радостно хлопать в ладоши только потому, что ты ЧТО-ТО сделал. Скопируют твой код люди не глядючи и кое-кто получит проблему на свою голову. Поэтому и указывают на недостатки в коде. А минус как предупреждение, чтобы хоть в комментарии заглянули отчего так.
AlexO; fomix; ACE$; +3 Ответить
9. apalon_pss (pavel_pss) 26.10.11 15:51
kosmo0 пишет:
(6)
Не кипи. Ты не в детском саду и здесь не будут радостно хлопать в ладоши только потому, что ты ЧТО-ТО сделал. Скопируют твой код люди не глядючи и кое-кто получит проблему на свою голову. Поэтому и указывают на недостатки в коде. А минус как предупреждение, чтобы хоть в комментарии заглянули отчего так.


глуп, тот программист, что просто тупо копирует чужой код, не разобравшись в нем. :)
10. Алексей (MarryJane) 27.10.11 00:49
Думаю что колонка Номерстроки есть не во всех таблиц значений.
11. Александр Медведев (anig99) 27.10.11 00:58
Проще всего создать временную таблицу, поместить туда таблицу значений + 1 колонка с цифрой 1 Свернуть всё это просуммировав колонку с цифрой 1 и отбор на итог >1. В результате получим таблицу дублей. При желании можно настроить не на все колонки, а только определенные.
12. apalon_pss (pavel_pss) 27.10.11 09:29
anig99 пишет:
Проще всего создать временную таблицу, поместить туда таблицу значений + 1 колонка с цифрой 1 Свернуть всё это просуммировав колонку с цифрой 1 и отбор на итог >1. В результате получим таблицу дублей. При желании можно настроить не на все колонки, а только определенные.


да согласен, тоже как вариант., единственное, что так ты не сможешь сообщить какие строки одинаковые, и пользователю придется искать по наименованию, а если в табл. 500 строк то это увеличит время поиска.
13. apalon_pss (pavel_pss) 27.10.11 09:33
MarryJane пишет:
Думаю что колонка Номерстроки есть не во всех таблиц значений.


если я не ошибаюсь, то ее может не быть только в том случе, если таб. ты создаешь сам кодом. А если это табл. часть док. то там эта колонка есть всегда.
14. direktorSan (direktorSan) 27.10.11 20:37
Согласен с (1) - небрежно.
1. Проверка в самом начале:

Если Тип(ТЧ) = Тип("ТаблицаЗначений") Тогда
ТаблЗнач = ТЧ;
Иначе
ТаблЗнач = ТЧ.Выгрузить();
КонецЕсли;

В комментарии к функции описано, что передаваться может или таблица значений или табличная часть документа.
Проверка на таблицу значений есть, а проверки на табличную часть нет. Непорядок.
Т.е. если мы передадим в параметре ТЧ значение типа СписокЗначений (в нем же тоже можно дубли поискать), то завал обеспечен.

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

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


(6) "Слабо/не слабо" - не аргумент и не ответ на критику. А критика реализации, на мой взгдяд, вполне конструктивная. И если учесть все то, что здесь говорилось, то можно написать очень даже неплохой и эффективный обработчик.

(11) Ваш вариант сработает только в том случае, если все колонки в ТЗ типизированы.
А данная реализация претендует на универсальность.
15. Александр Медведев (anig99) 27.10.11 21:55
(12) apalon_pss, Можно пойти дальше и отобранные строки дубль соединить с исходной таблицей по полной - тогда можно получить номера строк.
16. apalon_pss (pavel_pss) 28.10.11 12:59
Признаяюсь, код не был доведен до ума, по причине загруженности, ну и в моем случае просто не нужна была универсальность. Поэтому благодарю всех за обсуждение, ну а если кто то хочет предложить что то еще, то пожалуйста. Ну и может либо я сам, либо кто нить другой учтет все высказывания и выложит реально универсальный алгоритм, может кому и пригодится.

Знания бесценны и ими надо делиться, чтобы они совершенствовались - это так от себя :)
17. Артур Аюханов (artbear) 14.11.11 17:11
(0) Будем каждый программный "чих" сюда выкладывать? :(
Присоединяюсь к предудыщим словам - слабо, не все проверено, медленно, без ключевых полей дубли искать не смысла и т.п.
Минусую!
18. Артур Аюханов (artbear) 14.11.11 17:35
(12)
apalon_pss пишет:
единственное, что так ты не сможешь сообщить какие строки одинаковые, и пользователю придется искать по наименованию, а если в табл. 500 строк то это увеличит время поиска.

А кто мешает сделать еще один запрос к полученной таблице дублей и присоединить таблицу наименований?
и все это на сервере.
19. Артур Аюханов (artbear) 14.11.11 17:43
+(18) вот простой и не универсальный, но рабочий код
Функция ЕстьДублиТоваров()
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	|	ПеремещениетчТовары.Товар,
	|	ПеремещениетчТовары.Товар.Наименование,
	|	ПеремещениетчТовары.НомерСтроки
	|ПОМЕСТИТЬ Таблица
	|ИЗ
	|	Документ.Перемещение.тчТовары КАК ПеремещениетчТовары
	|ГДЕ
	|	ПеремещениетчТовары.Ссылка = &Ссылка
	|	И ПеремещениетчТовары.Товар <> ЗНАЧЕНИЕ(Справочник.Товары.ПустаяСсылка)
	|	И ПеремещениетчТовары.Товар.ВидТовара <> ЗНАЧЕНИЕ(Перечисление.ВидыТоваров.Услуга)
	|;
	|
	|ВЫБРАТЬ
	|	Таблица.Товар
	|ПОМЕСТИТЬ Дубли
	|ИЗ
	|	Таблица КАК Таблица
	|
	|СГРУППИРОВАТЬ ПО
	|	Таблица.Товар
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Таблица.НомерСтроки) > 1
	|;
	|
	|////////////////////////////////////////////////////////////­////////////////////
	|ВЫБРАТЬ
	|	Таблица.ТоварНаименование,
	|	Таблица.НомерСтроки
	|ИЗ
	|	Дубли КАК Дубли
	|		ЛЕВОЕ СОЕДИНЕНИЕ Таблица КАК Таблица
	|		ПО Дубли.Товар = Таблица.Товар";
	Запрос.УстановитьПараметр("Ссылка", Ссылка);
	Результат = Запрос.Выполнить();
	естьДубли = НЕ Результат.Пустой();
	
	Если естьДубли Тогда
		Выборка = Результат.Выбрать();
		Пока Выборка.Следующий() Цикл
			Сообщить("Повторение товара: "+СокрЛП(Выборка.ТоварНаименование)+ " в строке "+Выборка.НомерСтроки);
			Возврат истина;
		КонецЦикла;
	КонецЕсли; 
	
	Возврат естьДубли;	
КонецФункции
...Показать Скрыть
Oblachnov; Збянтэжаны Саўка; +2 1 Ответить
20. Владислав Охотников (OVladius) 02.09.13 12:18
Жесткий вариант конечно, юзать цикл.

Не проще так?)

Для каждого Строка Из Тз Цикл
НайденныеСтроки = Тз.НайтиСтроки(Новый Структура("Сотрудник", Строка.Сотрудник));

Если НайденныеСтроки.Количество()>1 Тогда

ОбщегоНазначения.СообщитьОбОшибке("По сотруднику "+Строка.Сотрудник+" имеются дубли!");

КонецЕсли;
КонецЦикла;

Здесь идет поиск дублей по 1 полю, но нет проблем в структуру добавить все поля.
21. freebsdd freebsdd (freebsdd) 14.11.13 06:29
Спасибо за обработку, молодец! У меня вопрос появился, быть может я не сталкивался с такой потребностью, но для чего такая обработка, если можно всё содержимое в ТаблицеЗначений (или её клоне) при сворачивании определить точное количество дублей, всего "пара" строк, м?
22. Павел С (pavel_pss) 14.11.13 12:53
(21) freebsdd, нужно было обрабатывать в нескольких видов документов табличные части и сообщить пользователю какая позиция в таблице является дублем, т.к. табличная часть могла быть от 10 до 300 позиций. А время работы запроса не значительно бы уменьшило время ожидания, а то и вообще не был бы эффективен.
23. Константин Куликов (Светлый ум) 17.12.13 08:20
(18) - верно только в случае для проверки документов "записанных", а если проверять из текущего документа - не будут проверены только что добавленные строки.
24. Константин Куликов (Светлый ум) 17.12.13 08:23
минус поставил не из вредности, а для того чтобы обратили внимание.
25. Константин Куликов (Светлый ум) 17.12.13 08:23
и не тратили свое драгоценное время.
26. program program (prodines) 28.02.14 14:17
Аналогичная функция: Удаление дублей из Таблицы значений:

Процедура УдалениеДублейСтрокТЗ(ТаблЗнач)

	МассивУдаляемыхДублей = Новый Массив;
	
	Отбор = Новый Структура();
	
	Для каждого Стр Из ТаблЗнач Цикл
		
		Если МассивУдаляемыхДублей.Найти(Стр) = Неопределено Тогда	
			
			Отбор.Очистить();
			Для каждого Колонки Из ТаблЗнач.Колонки Цикл
				Отбор.Вставить(Колонки.Имя, Стр[Колонки.Имя]);
			КонецЦикла;
			
			Строки = ТаблЗнач.НайтиСтроки(Отбор);
			
			Если Строки.Количество() > 1 Тогда	
				
				Для Сч=1 По Строки.Количество()-1 Цикл
					
					МассивУдаляемыхДублей.Добавить(Строки[Сч]);
					
				КонецЦикла;
				
			КонецЕсли;
		КонецЕсли; 
	КонецЦикла;	
	
	Для каждого Элем Из МассивУдаляемыхДублей Цикл
		
		ТаблЗнач.Удалить(Элем);
		
	КонецЦикла;	
	
КонецПроцедуры
...Показать Скрыть
27. Vertex (Vertex) 05.05.14 17:35
Товары.Свернуть("Номенклатура","Количество"); и ВСЕ :)
28. Василий Орлов (Bublik2011) 16.01.15 05:04
Вот тоже придумал,

Функция УбратьПовторенияВТЗ(ТаблицаЗначенийТЗ,ИмяКолонки)
djfhcgssnNP=Новый ТаблицаЗначений;
	Для каждого КолонкиNP Из ТаблицаЗначенийТЗ.Колонки Цикл
			djfhcgssnNP.Колонки.Добавить(КолонкиNP.Имя);
	КонецЦикла;
	Индекс = ТаблицаЗначенийТЗ.Количество() - 1; 
		Пока Индекс >= 0 Цикл 
			СтрокаТаблицаЗначенийТЗ=ТаблицаЗначенийТЗ[Индекс];


			Если СокрЛП(ТипЗнч(djfhcgssnNP.Найти(СтрокаТаблицаЗначенийТЗ.Abcdefа, ИмяКолонки)))="Не определено" Тогда		
				НоваяСтрока = djfhcgssnNP.Добавить();
				НоваяСтрока[ИмяКолонки] = СтрокаТаблицаЗначенийТЗ[ИмяКолонки];
					Для каждого КолонкиNPHN Из djfhcgssnNP.Колонки Цикл
							НоваяСтрока[КолонкиNPHN.Имя] = СтрокаТаблицаЗначенийТЗ[КолонкиNPHN.Имя];
					КонецЦикла;
			КонецЕсли;		
				
			Индекс = Индекс - 1; 
		КонецЦикла;		
     Возврат djfhcgssnNP;
КонецФункции // УбратьПовторенияВТЗ()
...Показать Скрыть
29. ренат васильев (SkyLink2012) 16.01.15 19:16
Гораздо быстрее удалить дубли в таблице, по моему мнению, при помощи запроса |ВЫБРАТЬ РАЗЛИЧНЫЕ

	  Запрос=новый Запрос;
	МВТ = Новый МенеджерВременныхТаблиц;
    Запрос.МенеджерВременныхТаблиц = МВТ;
 	Запрос.Текст="
|ВЫБРАТЬ
|	ТЗ1.*
|ПОМЕСТИТЬ ТЗ1
|ИЗ
|	&ТЗ1 КАК ТЗ1
|;
|ВЫБРАТЬ РАЗЛИЧНЫЕ
|		ТЗ1.*,		
|ИЗ ТЗ1 КАК ТЗ1
|;
|УНИЧТОЖИТЬ ТЗ1
|";
	Запрос.УстановитьПараметр("ТЗ1",ТаблицаЗначений);
	Результат = Запрос.Выполнить().Выгрузить();
	
...Показать Скрыть
30. Василий Орлов (Bublik2011) 16.01.15 19:20
Так ведь у меня несколько колонок, не одна. И "Выбрать Различные" не будет работать на строках неограниченной длины.

По крайней мере, сравнивая ТЗ со справочником по реквизиту "Строка неограниченной длины" выдавало ошибку "нельзя сравнивать поля бла-бла несовместимые типы"
31. ренат васильев (SkyLink2012) 16.01.15 23:57
(30) Bublik2011, разумеется. Вот между прочим, в посте (28) вероятно вместо
Если СокрЛП(ТипЗнч(djfhcgssnNP.Найти(СтрокаТаблицаЗначенийТЗ.Abcdefа, ИмяКолонки)))="Не определено" Тогда 


надо наверное

Если СокрЛП(ТипЗнч(djfhcgssnNP.Найти(СтрокаТаблицаЗначенийТЗ[ИмяКолонки], ИмяКолонки)))="Не определено" Тогда 


У меня синтакс контроль браузера ошибку нашел. Не знаю, может, неправильно.
32. Владимир Командровский (Wefast) 11.07.15 01:09
(29) SkyLink2012,

{Форма.Форма.Форма(26)}: Ошибка при вызове метода контекста (Выполнить)
Результат = Запрос.Выполнить().Выгрузить();
по причине:
{(6, 5)}: Тип не может быть выбран в запросе
<<?>>&ТЗ1 КАК ТЗ1
34. Oxana Coffeeholic (perepetulichka) 08.12.16 13:47
Взяла за основу код из статьи и убрала дублирование сообщений:

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

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


Как пишет автор, вдруг пригодится новичкам :)
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа