Таблица значений, быстрое удаление дублей строк

07.06.13

Разработка - Механизмы платформы 1С

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

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

 
Процедура ТаблицаЗначений_УдалитьДубли_ПоСпискуКолонок(ТаблицаДанных, СписокКолонокСтрокой, ОставитьОднуСтроку = Ложь)
	
	//Проверяем, что таблица данных не пустая
	Если ТаблицаДанных.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	//Создаем и Заполняем буферную таблицу
	БуфернаяТаблицаДанных  = ТаблицаДанных.Скопировать(,СписокКолонокСтрокой);
	
	//Заполняем список колонок, по которым будет осуществелн поиск дублей
	МассивКолонок = Новый Массив;
	Для Каждого Колонка Из БуфернаяТаблицаДанных.Колоники Цикл
		МассивКолонок.Добавить(Колонка.Имя);
	КонецЦикла;
	
	//Добавляем колонку "Счетчик" и заполняем ее занчением = 1
	БуфернаяТаблицаДанных.Колонки.Добавить("Счетчик");
	БуфернаяТаблицаДанных.ЗаполнитьЗначения(1,"Счетчик");
	БуфернаяТаблицаДанных.Свернуть(СписокКолонокСтрокой,"Счетчик");
	
	//Создаем и заполняем буферную таблицу для получения значений стчетчиков
	БуфернаяТаблицаСчетчик = БуфернаяТаблицаДанных.Скопировать();
	БуфернаяТаблицаСчетчик.Свернуть("Счетчик");
	
	//Удаляем строку со счетчиком = 1
	СтрокаГдеСчетчикРавно1 = БуфернаяТаблицаСчетчик.Найти(1, "Счетчик");
	Если НЕ СтрокаГдеСчетчикРавно1 = Неопределено Тогда
		БуфернаяТаблицаСчетчик.Удалить(СтрокаГдеСчетчикРавно1);
	КонецЕсли;
	
	//Удаляем дубли в таблице данных
	Для Каждого СтрокаТаблицыСчетчик Из БуфернаяТаблицаСчетчик Цикл
		//Находим  строки буферной таблицы, для каждого из значений счетчика
		ОтборПоСчетчику = Новый Структура();
		ОтборПоСчетчику.Вставить("Счетчик",СтрокаТаблицыСчетчик.Счетчик);
		НайденныеСтроки = БуфернаяТаблицаДанных.НайтиСтроки(ОтборПоСчетчику);
		//Для каждой такой строки, заполняем структуру отбора реальной таблицы по значениям колонок из буферной таблицы
		Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл
			//Заполняем отбор по массиву колонок
			ОтборУдаляемыхСтрок = Новый Структура();
			Для Каждого Колонка Из МассивКолонок Цикл
				ОтборУдаляемыхСтрок.Вставить(Колонка,НайденнаяСтрока[Колонка]);	
			КонецЦикла;
			//Находим строки реальной таблице по соответствующему отбору, и удаляем.
			ЭтоПерваяСтрока = Истина;
			НайденныеСтрокиДляУдаления = ТаблицаДанных.НайтиСтроки(ОтборУдаляемыхСтрок);
			Для Каждого НайденнаяСтрокаДляУдаления Из НайденныеСтрокиДляУдаления Цикл
				Если ОставитьОднуСтроку И ЭтоПерваяСтрока Тогда
					ЭтоПерваяСтрока = Ложь;
					Продолжить;
				КонецЕсли;
				ТаблицаДанных.Удалить(НайденнаяСтрокаДляУдаления);	
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

См. также

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    4625    dsdred    53    

73

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    5312    YA_418728146    25    

64

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6440    dsdred    36    

112

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18508    SeiOkami    46    

118

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12113    human_new    27    

74

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8850    YA_418728146    6    

141

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6290    sebekerga    54    

94

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    16021    SeiOkami    31    

103
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. kapustinag 07.06.13 18:21 Сейчас в теме
И какая получилась производительность? С какими вариантами сравнивали?
2. q_i 577 11.06.13 21:07 Сейчас в теме
Для Каждого Колонка Из БуфернаяТаблицаДанных.Колоники Цикл
ivangrant; user645801_yyyuuu123q; SP2000; Zircool; Vorobyov; +5 Ответить
3. bulpi 215 12.06.13 10:02 Сейчас в теме
Нужно пару слов про идею и алгоритм написать, а не только текст процедуры. Чем Ваш вариант лучше того, что я сейчас сяду и напишу за 10 минут на коленке?
4. CagoBHuK 32 13.06.13 09:53 Сейчас в теме
Алгоритм писателя на 1С 77. Проще всего:
ВЫБРАТЬ * ПОМЕСТИТЬ ВременнаяТаблица ИЗ &Таблица КАК ВременнаяТаблица;
ВЫБРАТЬ РАЗЛИЧНЫЕ * ИЗ ВременнаяТаблица
Bryuh; Dmitri93; strelvan; ProChelny; ilego; ivangrant; alsegor; 2711640; Bublik2011; Kserken; Азбука Морзе; higs; +12 Ответить
6. abe 12.08.13 12:07 Сейчас в теме
(4) CagoBHuK, нужно иметь в виду, что добавится передача/возврат данных на SQL.
8. CagoBHuK 32 12.08.13 12:15 Сейчас в теме
(6) В данном случае речь шла о таблице значений, которую на клиенте Вы никак не сможете получить. Использование метода "Скопировать" доступно только для универсальных коллекций значений, которые не могут существовать на клиенте. Иными словами сам код обработки предполагает серверный вызов.
9. abe 12.08.13 12:18 Сейчас в теме
(8) CagoBHuK, я и не спорю относительно серверного (сервера 1С) вызова, добавится вызов именно SQL сервера.
10. CagoBHuK 32 12.08.13 14:38 Сейчас в теме
(9) Вы считаете, что он будет длиться дольше, чем представленный алгоритм пересчета?
12. CeHbKA 300 14.01.14 09:41 Сейчас в теме
(4) CagoBHuK, это при условии что все поля строк одинаковые.

Если, например, в таблице 4 колонки и дубли удалять надо только по 3 колонкам, то вариант стар как мир и даже описан в синтакс-помощнике (осторожно, код в стиле "капитан очевидность"):

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

=)
burza; SIrina9; ivangrant; Ankiss; ya.Avoronov; +5 1 Ответить
17. nselyutin 09.04.20 08:53 Сейчас в теме
Предложу свой вариант, немного улучшенный (12) больше универсальности, про быстродействие так же не могу сказать, тесты не проводил:

Процедура УдалитьДублиСтрокИзТаблицы(ИсходнаяТаблица, СписокКолонок = "") Экспорт
	
	Если СписокКолонок = "" Тогда 
			
		КолонкиТаблицы = ИсходнаяТаблица.ВыгрузитьКолонки().Колонки;

		ИменаКолонок = Новый Массив;
		
		Для Каждого КолонкаТаблица Из КолонкиТаблицы Цикл
			
			Если КолонкаТаблица.Имя = "НомерСтроки" Тогда
				
				Продолжить;
				
			КонецЕсли;
			
			ИменаКолонок.Добавить(КолонкаТаблица.Имя);
			
		КонецЦикла;

	Иначе
		
		ИменаКолонок = СтрРазделить("СписокКолонок", ",", Ложь);

	КонецЕсли;
	
	ИндексВерхний = 0;
	
	Пока ИндексВерхний < ИсходнаяТаблица.Количество() Цикл
		
		Строка = ИсходнаяТаблица[ИндексВерхний];
		
		Отбор = Новый Структура();
		
		Для Каждого ИмяКолонки Из ИменаКолонок Цикл
		
			Отбор.Вставить(ИмяКолонки, Строка[ИмяКолонки]);
			
		КонецЦикла;	
        
        Строки = ИсходнаяТаблица.НайтиСтроки(Отбор);
        
        Если Строки.Количество() > 1 Тогда
			
			Индекс = 0;
			
			Пока Индекс < Строки.Количество() - 1 Цикл
				
				ИсходнаяТаблица.Удалить(Строки[Индекс]);    
				
				Индекс = Индекс + 1;
				
			КонецЦикла;
			
		Иначе
			
			ИндексВерхний = ИндексВерхний + 1;
			
		КонецЕсли;
		
    КонецЦикла;
	
КонецПроцедуры
Показать
5. Vorobyov 25 14.06.13 15:51 Сейчас в теме
Можно использовать и запрос, спорить не буду. Таблицу нужно будет подготовить - описать типы колонок, и также добавить колонку, по которой будет происходить суммирование.
Далее почти по тексту, только нужно будет выбирать не различные, а группировать по нужному списку колонок (строить запрос динимически), и добавить в запрос условие "Имеющие сумма(КолонкаСуммирования) = 1". (просто различные использовать нельзя из-за значений в других колонках таблицы). Далее эту сгруппированную временную таблицу нужно будет джойнить с первоначальной временной таблицей, в условиях связи прописывать равенство по выбранным полям (строить условие динамически), плюс нужно будет в случае если нужно не удалить все дублирующие строки, а оставить по одной из удаляемых тоже это предусмотреть.

Хотел еще сказать, что этот способ расчитан, на большое количество строк в таблице, и малое количество строк с дублями, для других случаев можно это делать быстрее другим способом.

Вариант написанный на коленке возможно будет эффективнее, если предоставится возможность, хотел бы на него посмотреть и возможно использовать с Вашего разрешения.
7. abe 12.08.13 12:09 Сейчас в теме
Я бы еще подумал над добавлением индекса(ов) в таблицу значений (по которой выполняется поиск).
ser6702; ImHunter; +2 Ответить
11. prodines 107 10.12.13 13:15 Сейчас в теме
Мой вариант:

МассивДублей = Новый Массив;
МассивСотрудников = Новый Массив; // проверяемое на дубль значение

Для каждого Строка Из ТЗНачисления Цикл

Если Строка.СпособРасчета = Перечисления.СпособыРасчетаОплатыТруда.СдельныйЗаработок Тогда // критерий сравнения на дубль, может быть любой

Элем = МассивСотрудников.Найти(Строка.Сотрудник);

Если Элем = Неопределено Тогда // это первое вхождение проверяемого на дубль сотрудника

МассивСотрудников.Добавить(Строка.Сотрудник);

Иначе // а это - уже мы дубль встретили, по данному сотруднику и по данному критерию проверки на дубль

МассивДублей.Добавить(Строка);

КонецЕсли;

КонецЕсли;

КонецЦикла;

Если МассивДублей.Количество() > 0 Тогда

Для каждого Элем Из МассивДублей Цикл

ТЗНачисления.Удалить(Элем);

КонецЦикла;

КонецЕсли;
Показать


Вся проверка делается за один проход. Очень просто.
isn; maikl007; Art39_; mart-sha; +4 Ответить
13. MaiorovYury 10 14.04.14 15:25 Сейчас в теме
14. DJDUH 17 14.04.14 15:57 Сейчас в теме
Ну как-бы есть и такой метод Таблицы Значений, как Свернуть("Групп колонки", " сумм колонки") если ничего не нужно суммировать, а только убрать дубли, подходит только групп колонки!
Слыхал наверное!?
15. logarifm 1118 29.11.19 18:52 Сейчас в теме
Так будет круче:

// Удаляет дубли строк из таблицы значений.
//
// Параметры:
//  ТаблицаИсточник           - ТаблицаЗначений - Таблица из которой необходимо удалить дубли строк
//  МассивИменКолонок         - Массив          - Имена колонок по которым производится поиск соответствий
//  ИмяКолонкиУказательСтроки - Строка          - Наименование колонки с указателем строки. 
//			Где значения указателя Тип Число, Строка
//			Например: указан параметр "НомерСтрокиФайла". Для каждой строки таблицы он равняется числам 1,2,3,4 и т.д.
//          При удалении дублирующих строк в исходящую таблицу будет добавлена колонка "УказательСтроки", где и будут размещены эти "идентификаторы/указатели". 
//          Колонка "НомерСтрокиФайла" будет содержать какое-то из значений.  
//          	"УказательСтроки"  = "4;1;2;3;" 
// 		        "НомерСтрокиФайла" = 4
//          Удаление происходит обратным перебором поэтому числа резмещены в таком порядке.
//
// Возвращаемое значение:
//   ТаблицаЗначений   - Содержит коллекцию имен входящих колонок.
//      Когда был указан параметр "ИмяКолонкиУказательСтроки" то добавляется колонка "УказательСтроки"
//
Функция УдалитьДублиСтрокИзТаблицыЗначений(ТаблицаИсточник, МассивИменКолонок, ИмяКолонкиУказательСтроки = "") Экспорт
	
	ИмяКлючевыхКолонок = СтрСоединить(МассивИменКолонок, ",");
	
	Если Не ПустаяСтрока(ИмяКолонкиУказательСтроки) Тогда
		ИмяКлючевыхКолонок = ИмяКлючевыхКолонок + "," + ИмяКолонкиУказательСтроки;
	КонецЕсли;	
	
	Таблица = ТаблицаИсточник.Скопировать(,ИмяКлючевыхКолонок);
	Если Не ПустаяСтрока(ИмяКолонкиУказательСтроки) Тогда
		Таблица.Колонки.Добавить("УказательСтроки");
	КонецЕсли;	
	
	СтруктураОтбора = Новый Структура;
	Для каждого Имя Из МассивИменКолонок Цикл
		СтруктураОтбора.Вставить(Имя);
	КонецЦикла;	
	
	ИндексСтроки = Таблица.Количество()-1;
	Пока Истина Цикл
		
		Если ИндексСтроки < 0 Тогда
			Прервать;
		КонецЕсли;
		
		СтрокаЗначений = Таблица.Получить(ИндексСтроки);
		ЗаполнитьЗначенияСвойств(СтруктураОтбора, СтрокаЗначений);
		
		НайденныеСтроки = Таблица.НайтиСтроки(СтруктураОтбора);
		Если НайденныеСтроки.Количество() > 1 Тогда
			
			Для каждого УдаляемаяСтрока Из НайденныеСтроки Цикл
				
				Если Таблица.Индекс(УдаляемаяСтрока) = ИндексСтроки Тогда
					Продолжить; //оставляем текущую строку 
				КонецЕсли;	
				
				//укажем какие указатели строк были удалены
				Если Не ПустаяСтрока(ИмяКолонкиУказательСтроки) Тогда
					
					ТекПозицияСтр = Формат(СтрокаЗначений[ИмяКолонкиУказательСтроки], "ЧГ=0");
					Если СтрНайти(СтрокаЗначений.УказательСтроки, ТекПозицияСтр) = 0 Тогда
						
						СтрокаЗначений.УказательСтроки = ТекПозицияСтр + ";";
						
					КонецЕсли;	
					
					СтрокаЗначений.УказательСтроки = СтрокаЗначений.УказательСтроки + Формат(УдаляемаяСтрока[ИмяКолонкиУказательСтроки],"ЧГ=0") + ";";
					
				КонецЕсли;	
				
				Таблица.Удалить(УдаляемаяСтрока);
				ИндексСтроки = ИндексСтроки - 1;//строк становится меньше (уменшаем индекс)
				
			КонецЦикла;	
			
		КонецЕсли;
		
		ИндексСтроки = ИндексСтроки - 1;
		
	КонецЦикла;
	
	Возврат Таблица;
КонецФункции	
Показать
16. burni4 87 27.12.19 10:02 Сейчас в теме
Предложу свой вариант, по быстродействию утверждать ничего не могу

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

КонецПроцедуры
Показать
skillful; GetNight; +2 Ответить
19. GetNight 46 04.09.20 11:51 Сейчас в теме
(16) как оказалось, сам решил эту задачу точно таким же способом), только с помощью оператора Сред вместо Лев+СтрДлина

    Для каждого СтрокаКолонки из ТекущаяТаблицаЗначений.Колонки цикл
        
        ПеречислениеВсехКолонок = ПеречислениеВсехКолонок + "," + СтрокаКолонки.Имя;
        
    КонецЦикла;
    
    Если ПеречислениеВсехКолонок = "" тогда
        Возврат;
    Иначе
        ПеречислениеВсехКолонок = Сред(ПеречислениеВсехКолонок, 2);
    КонецЕсли;
Показать
20. skillful 4 29.05.23 19:34 Сейчас в теме
(16) Время удаления дублей в 29000 строк заняло секунду. В отличии от других способов
18. o3ophuk 25.06.20 23:20 Сейчас в теме
Процедура норм! Автору лайк!
21. aleksey2 86 25.07.23 12:04 Сейчас в теме
что за шляпа?
Поле объекта не обнаружено (Колоники)
22. SweetAppleWine 22.02.24 12:50 Сейчас в теме
Вот так можно еще
Правда, не пробовал, где много строк

МассивИменКолонок = Новый Массив;

Для каждого Колонка Из ТЗДанные.Колонки Цикл
	МассивИменКолонок.Добавить(Колонка.Имя)
КонецЦикла;
ТЗДанные.Свернуть(СтрСоединить(МассивИменКолонок,","));

Показать
Оставьте свое сообщение