gifts2017

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

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

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

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

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

См. также

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

Комментарии

1. Александр Капустин (kapustinag) 07.06.13 18:21
И какая получилась производительность? С какими вариантами сравнивали?
2. q_i 11.06.13 21:07
Для Каждого Колонка Из БуфернаяТаблицаДанных.Колоники Цикл
Zircool; Vorobyov; +2 Ответить
3. bulpi bulpi (bulpi) 12.06.13 10:02
Нужно пару слов про идею и алгоритм написать, а не только текст процедуры. Чем Ваш вариант лучше того, что я сейчас сяду и напишу за 10 минут на коленке?
4. Данила Елистратов (CagoBHuK) 13.06.13 09:53
Алгоритм писателя на 1С 77. Проще всего:
ВЫБРАТЬ * ПОМЕСТИТЬ ВременнаяТаблица ИЗ &Таблица КАК ВременнаяТаблица;
ВЫБРАТЬ РАЗЛИЧНЫЕ * ИЗ ВременнаяТаблица
Bublik2011; Kserken; Азбука Морзе; higs; +4 Ответить 2
5. Евгений (Vorobyov) 14.06.13 15:51
Можно использовать и запрос, спорить не буду. Таблицу нужно будет подготовить - описать типы колонок, и также добавить колонку, по которой будет происходить суммирование.
Далее почти по тексту, только нужно будет выбирать не различные, а группировать по нужному списку колонок (строить запрос динимически), и добавить в запрос условие "Имеющие сумма(КолонкаСуммирования) = 1". (просто различные использовать нельзя из-за значений в других колонках таблицы). Далее эту сгруппированную временную таблицу нужно будет джойнить с первоначальной временной таблицей, в условиях связи прописывать равенство по выбранным полям (строить условие динамически), плюс нужно будет в случае если нужно не удалить все дублирующие строки, а оставить по одной из удаляемых тоже это предусмотреть.

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

Вариант написанный на коленке возможно будет эффективнее, если предоставится возможность, хотел бы на него посмотреть и возможно использовать с Вашего разрешения.
6. Андрей Бирюк (abe) 12.08.13 12:07
(4) CagoBHuK, нужно иметь в виду, что добавится передача/возврат данных на SQL.
7. Андрей Бирюк (abe) 12.08.13 12:09
Я бы еще подумал над добавлением индекса(ов) в таблицу значений (по которой выполняется поиск).
8. Данила Елистратов (CagoBHuK) 12.08.13 12:15
(6) В данном случае речь шла о таблице значений, которую на клиенте Вы никак не сможете получить. Использование метода "Скопировать" доступно только для универсальных коллекций значений, которые не могут существовать на клиенте. Иными словами сам код обработки предполагает серверный вызов.
9. Андрей Бирюк (abe) 12.08.13 12:18
(8) CagoBHuK, я и не спорю относительно серверного (сервера 1С) вызова, добавится вызов именно SQL сервера.
10. Данила Елистратов (CagoBHuK) 12.08.13 14:38
(9) Вы считаете, что он будет длиться дольше, чем представленный алгоритм пересчета?
11. program program (prodines) 10.12.13 13:15
Мой вариант:

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

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

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

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

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

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

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

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

КонецЕсли;

КонецЕсли;

КонецЦикла;

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

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

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

КонецЦикла;

КонецЕсли;


Вся проверка делается за один проход. Очень просто.
mart-sha; +1 Ответить
12. Арсений Прялкин (CeHbKA) 14.01.14 09:41
(4) CagoBHuK, это при условии что все поля строк одинаковые.

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

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

=)
ya.Avoronov; +1 Ответить
13. Юрий Майоров (MaiorovYury) 14.04.14 15:25
14. DUH Technolover (DJDUH) 14.04.14 15:57
Ну как-бы есть и такой метод Таблицы Значений, как Свернуть("Групп колонки", " сумм колонки") если ничего не нужно суммировать, а только убрать дубли, подходит только групп колонки!
Слыхал наверное!?
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа