Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Виктор_sql
Member

Откуда:
Сообщений: 98
Сравнение двух xml с помощью рекурсии. Что не так?

Всем здравствуйте, есть вопрос.
Существует набор xml-данных состоящий из двух похожих частей Original и Changed. В Original исходные данные, в Changed измененные. Сопоставлял их с помощью рекурсивной ф-ции, чтобы получить на выходе xml, в котором данные из Original записаны, если в Changed точно такие же. У остальных данных в Changed по сравнению с Original отлично значение атрибута RowFieldValue. Поэтому из Original такие строки попадают с добавленным атрибутом Action = upd.
При выполнении первого варианта кода процесс уходит в бесконечный цикл.
При выполнении второго варианта кода(задано др.имя переменной счетчика в цикле), на выходе десяток одинаковых строк. Что там не так? Не удается доделать, хотя на мой взгляд ф-ция неплохая там.
Кому интересно, исполняемый код приложен в архиве к сообщению

К сообщению приложен файл (Cod.rar - 4Kb) cкачать
21 мар 16, 15:37    [18959472]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 8807
Какие циклы и рекурсии? Сиквел не предназначен для сравнения XLM, но он умеет сравнивать наборы табличных данных. Превратите XML в таблицы и сравнивайте.
21 мар 16, 16:02    [18959622]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
Виктор_sql
Кому интересно, исполняемый код приложен в архиве к сообщению

Приложенный файл (Cod.rar - 4Kb)
Лучше текст вставить в теги SRC, а потом - Spoiler. Зачем файл? Не нужен файл.
21 мар 16, 16:29    [18959777]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Виктор_sql
Member

Откуда:
Сообщений: 98
Владислав Колосов,

автор
Сиквел не предназначен для сравнения XLM

К чему категоричность. Пример ф-ции позаимствован и адаптирован из статей по xquery для mssql. Да и сам способ интересен, почему нет
21 мар 16, 16:47    [18959878]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Виктор_sql
Member

Откуда:
Сообщений: 98
iap, хорошо, вставляю код с бесконечным циклом
+

--CREATE
ALTER
		 FUNCTION common.CompareXml_EditXml
									(
									   @xml1	XML = NULL
									  ,@xml2	XML = NULL
									  ,@result	XML = NULL
									)
										RETURNS VARCHAR(MAX)
AS 
BEGIN
  DECLARE @Msg NVARCHAR(MAX)
		 ,@Upd NVARCHAR(MAX)
		 ,@Del NVARCHAR(MAX)

  SET @Upd = 'upd'
  SET @Del = 'del'
    -- -------------------------------------------------------------
    -- Если одна из вх.переменных равна NULL, тогда выход из ф-ции
    -- -------------------------------------------------------------
    IF @xml1 IS NULL OR @xml2 IS NULL 
      RETURN 'Если одна из вх.переменных NULL, тогда выход из ф-ции'
 
    -- -------------------------------------------------------------
    -- Проверка соответствия имен элементов(нодов). Если не совпадают, тогда выход из ф-ции
    -- -------------------------------------------------------------
    IF  (SELECT @xml1.value('(local-name((/*)[1]))','VARCHAR(MAX)')) 
        <> 
        (SELECT @xml2.value('(local-name((/*)[1]))','VARCHAR(MAX)'))
	  BEGIN
		DECLARE @attValue1 NVARCHAR(MAX)
		RETURN 'Проверка соответствия имен элементов(нодов). Если не совпадают, тогда выход из ф-ции ' --??	+ CAST(@xml1 AS VARCHAR(MAX))
	  END	

    -- -------------------------------------------------------------
    -- Проверка соответствия значений элементов(нодов). Если не совпадают, тогда выход из ф-ции
    -- -------------------------------------------------------------
    DECLARE  @elValue1 VARCHAR(MAX)
			,@elValue2 VARCHAR(MAX)
    SELECT
			@elValue1 = @xml1.value('((/*)[1])','VARCHAR(MAX)'),
			@elValue2 = @xml2.value('data((/*)[1])','VARCHAR(MAX)')

    IF  @elValue1 <> @elValue2
        RETURN 'Проверка соответствия значений элементов(нодов). Если не совпадают, тогда выход из ф-ции'

    -- -------------------------------------------------------------
    -- Проверка соответствия количества атрибутов нодов. Если не совпадают, тогда выход из ф-ции
    -- -------------------------------------------------------------
    DECLARE  @attCnt1 INT
			,@attCnt2 INT
    SELECT
        @attCnt1 = @xml1.query('count(/*/@*)').value('.','INT'),
        @attCnt2 = @xml2.query('count(/*/@*)').value('.','INT')
    IF  @attCnt1 <> @attCnt2 -- Эту проверку пока закомментируем
      RETURN 'Проверка соответствия количества атрибутов нодов. Если не совпадают, тогда выход из ф-ции'

    -- -------------------------------------------------------------
	-- Соответсвие атрибутов атрибутам
	-- Здесь цикл для каждого атрибута в XML1 на проверку существующего такого же атрибута в XML2
	-- Если атрибут существует, проверяем на совпадение значения этих атрибутов
    -- -------------------------------------------------------------
    DECLARE @cnt INT
    DECLARE @attName VARCHAR(MAX)
    DECLARE @attValue VARCHAR(MAX)
    DECLARE @attValue_Changed VARCHAR(MAX)

    SELECT @cnt = 1
        
    WHILE @cnt <= @attCnt1 
	  BEGIN
        SELECT @attName = NULL
			  ,@attValue = NULL
			  ,@attValue_Changed = NULL

        SELECT
            @attName = @xml1.value(
									'local-name((/*/@*[sql:variable("@cnt")])[1])', 
									'varchar(MAX)'),
            @attValue = @xml1.value(
									'(/*/@*[sql:variable("@cnt")])[1]', 
									'varchar(MAX)')
        
		-- Проверка, существует ли атрибут в другом XML документе
        IF @xml2.exist(		'(/*/@*[local-name()=sql:variable("@attName")])[1]'		) = 0
          BEGIN  
			RETURN 'Проверка, существует ли атрибут в другом XML документе. Если не существует, тогда выход из ф-ции' + ' ::: ' + CAST(@xml2 AS NVARCHAR(MAX))
		  END	

        -- Проверка на совпадение значений схожих атрибутов
        SET @attValue_Changed = @xml2.value(	'(/*/@*[local-name()=sql:variable("@attName")])[1]', 'varchar(MAX)' )

		IF  @attValue <> @attValue_Changed		-- Если значение у схожих атрибутов не совпадает, тогда
		  BEGIN	------------------------------------------------------------------------------------------------------
			SET @xml1.modify('insert attribute Action {sql:variable("@Upd")} into (//ContentRow[@RowFieldValue=sql:variable("@attValue")])[1]')		-- Добавляем в ноду атрибут Action и ставим значение (например upd)           			
			SET @xml1.modify('replace value of(/*/@*[local-name()=sql:variable("@attName")])[1] with sql:variable("@attValue_Changed")')			-- Заменяем  значение в атрибуте исходного xml(Original) на другое значение из такого же атрибута в изменившемся xml(Changed)
			RETURN CAST(@xml1 AS VARCHAR(MAX))
		  END	------------------------------------------------------------------------------------------------------
		  	
        SET @cnt = @cnt + 1
      END

    -- -------------------------------------------------------------
    -- Соответствие кол-ва дочерних элементов
    -- -------------------------------------------------------------
    DECLARE @elCnt1 INT
		   ,@elCnt2 INT
    SELECT
        @elCnt1 = @xml1.query('count(/*/*)').value('.','INT'),
        @elCnt2 = @xml2.query('count(/*/*)').value('.','INT')

     IF @elCnt1 <> @elCnt2		-- Эту проверку пока закомментируем
       RETURN 'Проверка на соответствие кол-ва дочерних элементов. Если кол-ва не совпадают, тогда выход из ф-ции'

---------------------------------------------------------------------------------------------------------------------------      
    -- -------------------------------------------------------------
    -- Рекурсия для каждого дочернего элемента
    -- -------------------------------------------------------------
    DECLARE @x1  XML
		   ,@x2	 XML
		   ,@diff XML
		   --,@cnt INT

    
	SET @cnt = 1

    WHILE @cnt <= @elCnt1 
	  BEGIN
        SELECT @x1 = @xml1.query('/*/*[sql:variable("@cnt")]')
			  ,@x2 = @xml2.query('/*/*[sql:variable("@cnt")]')
         
		SET @Msg = common.CompareXml_EditXml( @x1, @x2, @result)		 
		IF @Msg IS NOT NULL
		  BEGIN
			SET @diff = CAST(@Msg AS XML)
			SET @result.modify('insert sql:variable("@diff") as last into (/)[1]')
		  END --IF (@Msg
		  	--ELSE  SET @result.modify('insert sql:variable("@X1") as last into (/)[1]')
		--RETURN CAST(@result AS VARCHAR(MAX))		
		
        SET @cnt = @cnt + 1
      END
    RETURN CAST(@result AS NVARCHAR(MAX))

END
--------------------------------------------------------------------------
GO


DECLARE 
	    @xml_all XML
	   ,@xml1	 XML
	   ,@xml2	 XML
	   ,@result  XML

SET @xml_all = 	'<?xml version="1.0" encoding="WINDOWS-1251"?>
				<Root>
					<Original>
						<Content>
							<ContentRow RowFieldName="iddoc" RowFieldValue="15017"/>
							<ContentRow RowFieldName="typedoc_" RowFieldValue="Удостоверение личности"/>
							<ContentRow RowFieldName="series" RowFieldValue=""/>
							<ContentRow RowFieldName="number" RowFieldValue=""/>
							<ContentRow RowFieldName="bdate" RowFieldValue="01.07.2000"/>
							<ContentRow RowFieldName="date" RowFieldValue="28.07.2015 12:24:35"/>
							<ContentRow RowFieldName="note" RowFieldValue=""/>
							<ContentRow RowFieldName="fktypedoc" RowFieldValue="-2079757161"/>
							<ContentRow RowFieldName="ts" RowFieldValue="3541641"/>
							<ContentRow RowFieldName="count_doc" RowFieldValue=""/>
							<ContentRow RowFieldName="count_sub" RowFieldValue="">
								<ContentRow RowFieldName="fksub" RowFieldValue="5457">
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22822">
										<ContentRow RowFieldName="misnote" RowFieldValue="Ибрагимов"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22823">
										<ContentRow RowFieldName="misnote" RowFieldValue="С"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22824">
										<ContentRow RowFieldName="misnote" RowFieldValue="А"/>
									</ContentRow>
								</ContentRow>
							</ContentRow>
							<ContentRow RowFieldName="reg_block" RowFieldValue="">
								<ContentRow RowFieldName="numinput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateinput" RowFieldValue=""/>
								<ContentRow RowFieldName="numoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="datecontrol" RowFieldValue=""/>
								<ContentRow RowFieldName="fksender" RowFieldValue=""/>
								<ContentRow RowFieldName="fkrecipient" RowFieldValue=""/>
								<ContentRow RowFieldName="fkexecutor" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_drg" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="frs_block" RowFieldValue="">
								<ContentRow RowFieldName="frsdate" RowFieldValue=""/>
								<ContentRow RowFieldName="frsnum" RowFieldValue=""/>
								<ContentRow RowFieldName="fkregorg" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_af" RowFieldValue=""/>
							</ContentRow>
						</Content>
					</Original>
					<Changed>
						<Content>
							<ContentRow RowFieldName="iddoc" RowFieldValue="111"/>
							<ContentRow RowFieldName="typedoc_" RowFieldValue="Удостоверение личности_NNN"/>
							<ContentRow RowFieldName="series" RowFieldValue=""/>
							<ContentRow RowFieldName="number" RowFieldValue=""/>
							<ContentRow RowFieldName="bdate" RowFieldValue="01.07.2000"/>
							<ContentRow RowFieldName="date" RowFieldValue="28.07.2015 12:24:35"/>
							<ContentRow RowFieldName="note" RowFieldValue=""/>
							<ContentRow RowFieldName="fktypedoc" RowFieldValue="-2079757161"/>
							<ContentRow RowFieldName="ts" RowFieldValue="3541641"/>
							<ContentRow RowFieldName="count_doc" RowFieldValue=""/>
							<ContentRow RowFieldName="count_sub" RowFieldValue="">
								<ContentRow RowFieldName="fksub" RowFieldValue="5457">
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22822">
										<ContentRow RowFieldName="misnote" RowFieldValue="Ибрагимов"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22823">
										<ContentRow RowFieldName="misnote" RowFieldValue="С"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22824">
										<ContentRow RowFieldName="misnote" RowFieldValue="А"/>
									</ContentRow>
								</ContentRow>
							</ContentRow>
							<ContentRow RowFieldName="reg_block" RowFieldValue="">
								<ContentRow RowFieldName="numinput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateinput" RowFieldValue=""/>
								<ContentRow RowFieldName="numoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="datecontrol" RowFieldValue=""/>
								<ContentRow RowFieldName="fksender" RowFieldValue=""/>
								<ContentRow RowFieldName="fkrecipient" RowFieldValue=""/>
								<ContentRow RowFieldName="fkexecutor" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_drg" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="frs_block" RowFieldValue="">
								<ContentRow RowFieldName="frsdate" RowFieldValue=""/>
								<ContentRow RowFieldName="frsnum" RowFieldValue=""/>
								<ContentRow RowFieldName="fkregorg" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_af" RowFieldValue=""/>
							</ContentRow>
						</Content>
					</Changed>
				</Root>'

SET @xml1	= @xml_all.query('//Original/Content')
SET @xml2	= @xml_all.query('//Changed/Content')
SET @result = '' --'<Content></Content>'

SELECT common.CompareXml_EditXml( 
								  @xml1
								 ,@xml2
								 ,@result 
								 )

21 мар 16, 16:57    [18959977]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 8807
Виктор_sql,

категорично потому, что Вы путаете "можно" и "нужно". Да, существует возможность выполнения xqeury запросов, но с этим лучше справится другая система. Т.е. прибегать к обработке XML стоит лишь в определенных случаях и уж точно не в задачах, не относящихся к функциям СУБД.
21 мар 16, 17:14    [18960086]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
aleks2
Guest
Виктор_sql
Что там не так?

Генетический код подкачал.
Но это можно исправить отладчиком.


Виктор_sql
iap, хорошо, вставляю код с бесконечным циклом

Осподе, милосердный!
Нафига эта императивщина?
Сказали же
XML -> плоская таблица(ы) и сравнивайте до посинения одним селектом.
23 мар 16, 05:47    [18966782]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
LexusR
Member

Откуда: Novosibirsk
Сообщений: 1887
DECLARE @xml1 xml
DECLARE @xml2 xml
DECLARE @xml3 xml
DECLARE @hDoc 			INT;

select @xml1 = '<root><level1 id1="1"><level2 id2="20"/><level2 id2="21"/></level1></root>'
select @xml2 = '<root><level1 id1="1"><level2 id2="20"/><level2 id2="22"/></level1></root>'
select @xml3 = '<root><level1 id1="1"><level2 id2="20"/></level1></root>'

IF OBJECT_ID('tempdb..#tab1') is not null drop table #tab1
EXEC sp_xml_preparedocument @hDoc OUTPUT, @xml1
SELECT * INTO #tab1 FROM OPENXML(@hDoc,'*')
EXEC sp_xml_removedocument @hDoc;

IF OBJECT_ID('tempdb..#tab2') is not null drop table #tab2
EXEC sp_xml_preparedocument @hDoc OUTPUT, @xml2
SELECT * INTO #tab2 FROM OPENXML(@hDoc,'*')
EXEC sp_xml_removedocument @hDoc;

IF OBJECT_ID('tempdb..#tab3') is not null drop table #tab3
EXEC sp_xml_preparedocument @hDoc OUTPUT, @xml3
SELECT * INTO #tab3 FROM OPENXML(@hDoc,'*')
EXEC sp_xml_removedocument @hDoc;


if exists(select * from #tab1 t1 full outer join #tab2 t2 on t1.id = t2.id
where t1.id is null or t2.id is null or t1.parentid <> t2.parentid  or t1.localname <> t2.localname)
  print('структура xml1 и xml2 не совпадают')
if exists(select * from #tab1 t1 full outer join #tab3 t2 on t1.id = t2.id
where t1.id is null or t2.id is null or t1.parentid <> t2.parentid  or t1.localname <> t2.localname)
  print('структура xml1 и xml3 не совпадают')

-- разница в значениях атрибутов
select p.localname
, cast(t1.[text] as varchar(max)) as value1
, cast(t2.[text] as varchar(max)) as value2
from #tab1 t1 join #tab2 t2 on t1.id = t2.id 
join #tab1 p on p.id = t1.parentid 
where cast(t1.[text] as varchar(max))<>cast(t2.[text] as varchar(max))
23 мар 16, 07:41    [18966832]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6201
LexusR
t1.localname <> t2.localname

namespaceuri тоже надо бы сравнивать
23 мар 16, 09:25    [18967009]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
invm
Member

Откуда: Москва
Сообщений: 9827
Виктор_sql
чтобы получить на выходе xml, в котором данные из Original записаны, если в Changed точно такие же. У остальных данных в Changed по сравнению с Original отлично значение атрибута RowFieldValue. Поэтому из Original такие строки попадают с добавленным атрибутом Action = upd.
+
declare @x xml = '<?xml version="1.0" encoding="WINDOWS-1251"?>
				<Root>
					<Original>
						<Content>
							<ContentRow RowFieldName="iddoc" RowFieldValue="15017"/>
							<ContentRow RowFieldName="typedoc_" RowFieldValue="Удостоверение личности"/>
							<ContentRow RowFieldName="series" RowFieldValue=""/>
							<ContentRow RowFieldName="number" RowFieldValue=""/>
							<ContentRow RowFieldName="bdate" RowFieldValue="01.07.2000"/>
							<ContentRow RowFieldName="date" RowFieldValue="28.07.2015 12:24:35"/>
							<ContentRow RowFieldName="note" RowFieldValue=""/>
							<ContentRow RowFieldName="fktypedoc" RowFieldValue="-2079757161"/>
							<ContentRow RowFieldName="ts" RowFieldValue="3541641"/>
							<ContentRow RowFieldName="count_doc" RowFieldValue=""/>
							<ContentRow RowFieldName="count_sub" RowFieldValue="">
								<ContentRow RowFieldName="fksub" RowFieldValue="5457">
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22822">
										<ContentRow RowFieldName="misnote" RowFieldValue="Ибрагимов"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22823">
										<ContentRow RowFieldName="misnote" RowFieldValue="С"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22824">
										<ContentRow RowFieldName="misnote" RowFieldValue="А"/>
									</ContentRow>
								</ContentRow>
							</ContentRow>
							<ContentRow RowFieldName="reg_block" RowFieldValue="">
								<ContentRow RowFieldName="numinput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateinput" RowFieldValue=""/>
								<ContentRow RowFieldName="numoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="datecontrol" RowFieldValue=""/>
								<ContentRow RowFieldName="fksender" RowFieldValue=""/>
								<ContentRow RowFieldName="fkrecipient" RowFieldValue=""/>
								<ContentRow RowFieldName="fkexecutor" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_drg" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="frs_block" RowFieldValue="">
								<ContentRow RowFieldName="frsdate" RowFieldValue=""/>
								<ContentRow RowFieldName="frsnum" RowFieldValue=""/>
								<ContentRow RowFieldName="fkregorg" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_af" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="iddoc" RowFieldValue="1501701"/>
						</Content>
					</Original>
					<Changed>
						<Content>
							<ContentRow RowFieldName="iddoc" RowFieldValue="111"/>
							<ContentRow RowFieldName="typedoc_" RowFieldValue="Удостоверение личности_NNN"/>
							<ContentRow RowFieldName="series" RowFieldValue=""/>
							<ContentRow RowFieldName="number" RowFieldValue=""/>
							<ContentRow RowFieldName="bdate" RowFieldValue="01.07.2000"/>
							<ContentRow RowFieldName="date" RowFieldValue="28.07.2015 12:24:35"/>
							<ContentRow RowFieldName="note" RowFieldValue=""/>
							<ContentRow RowFieldName="fktypedoc" RowFieldValue="-2079757161"/>
							<ContentRow RowFieldName="ts" RowFieldValue="3541641"/>
							<ContentRow RowFieldName="count_doc" RowFieldValue=""/>
							<ContentRow RowFieldName="count_sub" RowFieldValue="">
								<ContentRow RowFieldName="fksub" RowFieldValue="5457">
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22822">
										<ContentRow RowFieldName="misnote" RowFieldValue="Ибрагимов"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22823">
										<ContentRow RowFieldName="misnote" RowFieldValue="Г"/>
									</ContentRow>
									<ContentRow RowFieldName="subj_rel_elm" RowFieldValue="22824">
										<ContentRow RowFieldName="misnote" RowFieldValue="А"/>
									</ContentRow>
								</ContentRow>
							</ContentRow>
							<ContentRow RowFieldName="reg_block" RowFieldValue="">
								<ContentRow RowFieldName="numinput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateinput" RowFieldValue=""/>
								<ContentRow RowFieldName="numoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="dateoutput" RowFieldValue=""/>
								<ContentRow RowFieldName="datecontrol" RowFieldValue=""/>
								<ContentRow RowFieldName="fksender" RowFieldValue=""/>
								<ContentRow RowFieldName="fkrecipient" RowFieldValue=""/>
								<ContentRow RowFieldName="fkexecutor" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_drg" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="frs_block" RowFieldValue="">
								<ContentRow RowFieldName="frsdate" RowFieldValue=""/>
								<ContentRow RowFieldName="frsnum" RowFieldValue=""/>
								<ContentRow RowFieldName="fkregorg" RowFieldValue=""/>
								<ContentRow RowFieldName="ts_af" RowFieldValue=""/>
							</ContentRow>
							<ContentRow RowFieldName="iddoc" RowFieldValue="1501702"/>
						</Content>
					</Changed>
				</Root>';

declare @t table (FieldName varchar(100), FieldValueOld varchar(100), FieldValueNew varchar(100), Action varchar(10), p int);
declare @o table (id bigint, parentid bigint, nodetype int, localname nvarchar(100), text nvarchar(max));
declare @c table (id bigint, parentid bigint, nodetype int, localname nvarchar(100), text nvarchar(max));

declare @h int;

exec sp_xml_preparedocument @h output, @x;
insert into @o
 (id, parentid, nodetype, localname, text)
 select id, parentid, nodetype, localname, text from openxml(@h, '/Root/Original/Content//ContentRow');
exec sp_xml_removedocument @h;

exec sp_xml_preparedocument @h output, @x;
insert into @c
 (id, parentid, nodetype, localname, text)
 select id, parentid, nodetype, localname, text from openxml(@h, '/Root/Changed/Content//ContentRow');
exec sp_xml_removedocument @h;

with o as
(
 select
  c.FieldName, e.FieldValue, row_number() over (partition by c.FieldName order by a.id) as rn
 from
  (select id from @o where nodetype = 1 and localname = N'ContentRow') a cross apply
  (select id from @o where nodetype = 2 and parentid = a.id and localname = 'RowFieldName') b cross apply
  (select [text] from @o where nodetype = 3 and parentid = b.id and localname = '#text') c(FieldName) cross apply
  (select id from @o where nodetype = 2 and parentid = a.id and localname = 'RowFieldValue') d cross apply
  (select [text] from @o where nodetype = 3 and parentid = d.id and localname = '#text') e(FieldValue)
),
c as
(
 select
  c.FieldName, e.FieldValue, row_number() over (partition by c.FieldName order by a.id) as rn
 from
  (select id from @c where nodetype = 1 and localname = N'ContentRow') a cross apply
  (select id from @c where nodetype = 2 and parentid = a.id and localname = 'RowFieldName') b cross apply
  (select [text] from @c where nodetype = 3 and parentid = b.id and localname = '#text') c(FieldName) cross apply
  (select id from @c where nodetype = 2 and parentid = a.id and localname = 'RowFieldValue') d cross apply
  (select [text] from @c where nodetype = 3 and parentid = d.id and localname = '#text') e(FieldValue)
)
insert into @t
 (FieldName, FieldValueOld, FieldValueNew, Action, p)
select
 o.FieldName, o.FieldValue, c.FieldValue,
 case when c.FieldValue is not null then 'upd' end,
 row_number() over (partition by o.FieldName, o.FieldValue order by (select 1))
from
 o join
 c on c.FieldName = o.FieldName and c.rn = o.rn
where
 c.FieldValue <> o.FieldValue;

declare @FieldName varchar(100), @FieldValueOld varchar(100), @FieldValueNew varchar(100), @Action varchar(10), @p int;

declare c cursor local fast_forward for
 select FieldName, FieldValueOld, FieldValueNew, Action, p from @t;

open c;
while 1 = 1
 begin
  fetch next from c into @FieldName, @FieldValueOld, @FieldValueNew, @Action, @p;
  if @@fetch_status <> 0
   break;

  set @x.modify('insert attribute Action {sql:variable("@Action")} into (((/Root/Original/Content//ContentRow[@RowFieldName = sql:variable("@FieldName") and @RowFieldValue = sql:variable("@FieldValueOld")])[position() = sql:variable("@p")])[1])');
  set @x.modify('replace value of (((/Root/Original/Content//ContentRow[@RowFieldName = sql:variable("@FieldName") and @RowFieldValue = sql:variable("@FieldValueOld")])[position() = sql:variable("@p")])[1]/@RowFieldValue) with sql:variable("@FieldValueNew")');
 end;

close c;
deallocate c;

select @x;
23 мар 16, 23:31    [18971055]     Ответить | Цитировать Сообщить модератору
 Re: Сравнение двух xml с помощью рекурсии. Что не так?  [new]
Виктор_sql
Member

Откуда:
Сообщений: 98
Благодарю за содержательные ответы
24 мар 16, 06:50    [18971489]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить