gifts2017

Сравнение таблиц значений (табличных частей)

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

Задача такова, сравнить 2 таблицы значений (Табличные части) и выдать результат, о том что в них изменилось, применительно к реальной задаче будет звучать так, показать пользователю что изменилось в документе.

Всем Привет!

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

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

Задача такова, сравнить 2 таблицы значений (Табличные части) и выдать результат, о том что в них изменилось, применительно к реальной задаче будет звучать так, показать пользователю что изменилось в документе. Допустим пусть это будут 2 разные версии объектов записанные в ИБ, конечно многие могут сказать (или подумать) может быть научить пользователя пользоваться обработкой История изменений объектов. Всё конечно хорошо в теории, но История изменения объектов (допустим УПП 1.3.38.3) обладает рядом недостатков, а именно:

  1. При версии объекта больше 999 при работе вызывает ошибку, оно и понятно разработчики не позаботились о корректном переводе (Строка – Число и обратно), но да бог с ними, не об этом речь.
  2. Обработка сравнивает табличные части по индексам, т.е. если строка будет перемещена с 1-й на 3-ю позицию, обработка покажет изменения, а они были? Тоже думаю что нет.

Накидал небольшой кусочек кода, как выкрутится из такой ситуации.

  1. Дано:

Таблица значений эталон, с ней будем сравнивать.

 

 

Таблица значений, которую  будем сравнивать.

 

Колонки «ВесСтроки, Идентификатор,БылВыбран» были добавлены для успешного анализа.

 2. Думаю описывать заполнение таблиц нет смысла, они должны у нас уже быть по задаче, отмечу только что для успешной обработки нам понадобится дополнительная структура «СтруктураВеса».

Дело в том что, при таком подходе мы сможем более  гибко настроить алгоритм для соответствия строк.

 

Возьмём пример из таблиц Выше

Строка Номер 2 в обоих таблицах содержит всего 1 изменение , а если их будет 2 и более ?

Ответ прост, при таком подходе мы можем легко настроить алгоритм так, что при совпадении только по номенклатуре  строки сопоставятся или нет, а если изменят только номенклатуру и остальные значения в строке будут нетронутыми сопоставлять ли такие строки или нет? Именно за этим мы и делаем табличку с весом значимости колонки.

 3. Так же нам понадобится таблица куда мы будем записывать результат работы алгоритма, рисунок уже после отработки алгоритма.

 

Как видим имея такую табличку, соединяя через неё сравниваемые таблицы мы всегда можем показать пользователям, что было изменено, добавлено, удалено, осталось без изменений и это всё без привязки к конкретной строке, анализируются все строки.

 

Собственно весь код ниже, так же выкладываю обработку с примером.


Функция ПровестиАнализ(ТЗИсточник,ТЗПриемник,ТЗАнализ,МассивКолонок,СтруктураВеса)

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

       
ТЗПриемник.Сортировать("ВесСтроки Убыв, БылВыбран Убыв");

        Для Каждого
СтрокаПоиска из ТЗПриемник Цикл
            Если
СтрокаПоиска.БылВыбран Тогда
                Продолжить;
            КонецЕсли;

            Если СтрокаПоиска.ВесСтроки = МаксимальныйПорог Тогда
                СтрокаПоиска.БылВыбран = Истина;
               
НоваяСтрока = ТЗАнализ.Добавить();
               
НоваяСтрока.ИдентификаторИсточник = СтрокаПоиска.Идентификатор;
               
НоваяСтрока.ИдентификаторПриемник = Строка.Идентификатор;
               
НоваяСтрока.Состояние = "Совпадение";
                Прервать;
            ИначеЕсли
СтрокаПоиска.ВесСтроки >= МинимальныйПорог Тогда
               
СтрокаПоиска.БылВыбран = Истина;
               
НоваяСтрока = ТЗАнализ.Добавить();
               
НоваяСтрока.ИдентификаторИсточник = СтрокаПоиска.Идентификатор;
               
НоваяСтрока.ИдентификаторПриемник = Строка.Идентификатор;
               
НоваяСтрока.Состояние = "Изменен";
                Прервать;
            ИначеЕсли
СтрокаПоиска.ВесСтроки < МинимальныйПорог Тогда
               
НоваяСтрока = ТЗАнализ.Добавить();
               
НоваяСтрока.ИдентификаторИсточник = СтрокаПоиска.Идентификатор;
               
НоваяСтрока.Состояние = "Удален";
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;

   
Отбор = Новый Структура;
   
Отбор.Вставить("БылВыбран", Ложь);
   
НайденныеСтроки = ТЗПриемник.НайтиСтроки(Отбор);

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

    Возврат
ТЗАнализ;

КонецФункции
// ПровестиАнализ(ТЗИсточник,ТЗПриемник,ТЗАнализ)

 

 

 

 

Надеюсь статья была полезной.

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
ПримерСравниванияТЗПоСтрокамСИспользованиемУдельногоВеса.epf
.epf 7,05Kb
06.12.13
7
.epf 7,05Kb 7 Скачать

См. также

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

Комментарии

1. Айрат Саттаров (kwazi) 07.12.13 12:56
в бсп есть подсистема версионирование и есть отчет по сравнению версий там уже все есть
2. Александр Крынецкий (echo77) 07.12.13 19:28
(1) Автор пишет о существовании нескольких багов этого отчета.
Я бы взял этот отчет за основу и допилил
3. Denis j (Xatori111) 07.12.13 22:11
(2)Это всего лишь пример как можно сравнить таблицы, а отчёт допилить не проблема, вопрос надо ли?
Дмитрий74Чел; +1 Ответить
4. Яков Коган (Yashazz) 08.12.13 12:07
1. Таких сравнивалок для таблиц значений уже было вагон, и я не понимаю, почему авторы почти всех их делают НЕ через запрос.
2. Если делать не для себя, а для пользователя, то я бы предложил игры с самим интерфейсом документа (например, изменить фон нужных полей и ячеек табчастей другим цветом, для изменившихся/добавленных итд, вопрос только про удалённые).
5. Сергей Ожерельев (Поручик) 09.12.13 07:58
(4) Сделай через запрос, оценим поделие.
Evil Beaver; +1 Ответить 1
6. Яков Коган (Yashazz) 09.12.13 23:23
(5) Изволите желать через СКД, да с наборами, аль бо по-простому, в обычном запросе? )))
7. Сергей Ожерельев (Поручик) 09.12.13 23:55
(6) Сделайте весь список, пожалуйста. Я бы не отказался посмотреть на СКД с наборами
8. Борис (soap) 11.12.13 15:14
Для обычного запроса как то так простейший пример

ТЗ1=Новый ТаблицаЗначений ;
ТЗ2=Новый ТаблицаЗначений;
ТЗ1.Колонки.Добавить("П1");
ТЗ1.Колонки.Добавить("П2");
ТЗ1.Колонки.Добавить("П3");
ТЗ2.Колонки.Добавить("П1");
ТЗ2.Колонки.Добавить("П2");
ТЗ2.Колонки.Добавить("П3");

Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ТЗ1.П1,ТЗ2.П1) КАК П1,
| ЕСТЬNULL(ТЗ1.П1,1) КАК Удалено,
| ЕСТЬNULL(ТЗ2.П1,2) КАК Добавлено,
| ТЗ1.П2 КАК П2_Начальное ,
| ТЗ2.П2 КАК П2_Текущее ,
| ТЗ1.П3 КАК П3_Начальное ,
| ТЗ2.П3 КАК П3_Текущее,
| ВЫБОР
| Когда ТЗ1.П2<>ТЗ2.П2
| Тогда 3
| ИНАЧЕ 4
| КОНЕЦ КАК ИзмененоП2,
| ВЫБОР
| Когда ТЗ1.П3<>ТЗ2.П3
| Тогда 3
| ИНАЧЕ 4
| КОНЕЦ КАК ИзмененоП3
| ИЗ
| ТЗ1 КАК ТЗ1
| ПОЛНОЕ СОЕДИНЕНИЕ ТЗ2 КАК ТЗ2
| ПО ТЗ2.П1=ТЗ2.П1";
9. Денис (amyd) 12.12.13 10:03
тоже самое, но на основе запросов, все же перебирать табличные части, пускай и в цикле, "мовитон", особенно если ТЗ за 5000 записей.
сначала хотел вывести "контр публикацию" с ссылкой на эту, но уж очень хорошо расписана, поэтому просто дополню в комментарии своей процедурой сравнения на основе запросов
функция ТабличныеЧастиРазныеУлучшенная(ТаблицаДо,ТаблицаПосле,ТаблицаРасхождений=неопределено) экспорт
	//ТаблицаДо=док.Товары.Выгрузить();
	//	ТаблицаПосле=док.Товары.Выгрузить();
	ТекстЗапроса="Выбрать ";	
	
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		ТекстЗапроса = ТекстЗапроса +"
		| ТТДо."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса=Лев(ТекстЗапроса,стрдлина(ТекстЗапроса)-1);
	ТекстЗапроса = ТекстЗапроса+"
	|поместить ТаблоДо 
	|из 
	|	&ТабДо как ТТДо
	|;	
	|Выбрать";
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		
		ТекстЗапроса = ТекстЗапроса +"
		| ТТПосле."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса=Лев(ТекстЗапроса,стрдлина(ТекстЗапроса)-1);	
	ТекстЗапроса = ТекстЗапроса+"
	|поместить ТаблоПосле 
	|из 
	|	&ТабПосле как ТТПосле
	|;
	|выбрать" ;
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		
		ТекстЗапроса = ТекстЗапроса +"
		| Сборочный."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса = ТекстЗапроса+"
	| ЕстьNULL(сумма(Сборочный.Контроль),0) как Контроль
	|из 
	|	(
	|выбрать " ;
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		
		ТекстЗапроса = ТекстЗапроса +"
		| ТаблоДо."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса = ТекстЗапроса+"
	|   -1 как Контроль
	| из  ТаблоДо как ТаблоДо
	|	 
	|Объединить все
	|
	| Выбрать ";
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		
		ТекстЗапроса = ТекстЗапроса +"
		| ТаблоПосле."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса = ТекстЗапроса+"
	|   1
	| из  ТаблоПосле как ТаблоПосле
	| ) как Сборочный
	| сгруппировать по";
	для каждого эл из ТаблицаДо.Колонки цикл
		если эл.Имя="КлючСтроки" тогда
			продолжить;
		конецесли;
		
		ТекстЗапроса = ТекстЗапроса +"
		| Сборочный."+эл.Имя+",";
	конеццикла;
	ТекстЗапроса=Лев(ТекстЗапроса,стрдлина(ТекстЗапроса)-1);	
	
	запр = новый Запрос(ТекстЗапроса);
	
	Запр.УстановитьПараметр("ТабДо",ТаблицаДо);
	Запр.УстановитьПараметр("ТабПосле",ТаблицаПосле);
	
	ВЫЗаДН = запр.Выполнить().Выгрузить();
	ТаблицаРасхождений=ВЫЗаДН.Скопировать();
	если ВЫЗаДН.найти(1,"контроль")<> неопределено или ВЫЗаДН.найти(-1,"контроль")<>неопределено тогда
		возврат истина;
	иначе
		возврат ложь;
	конецесли;
	
	
конецфункции	
...Показать Скрыть
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа