简体   繁体   中英

SQL Server Update Value XML Node

I have 2 tables in SQL Server

Table1

   ID   - Name  - Phone
   1      HK      999    
   2      RK      888
   3      SK      777
   4      PK      666

Table2

   ID   - XMLCol
   1      XMLVal1

XMLVal1

   <Root>
    <Data1>
     <ID>1</ID>
     <Name>HK</Name> 
     </Data1>
    <Data1>
     <ID>2</ID>
     <Name>RK</Name>
     </Data1>
    </Root>

Now I am inserting a GUID column into Table1

Table1

   ID   - Name  - Phone  - GUID
   1      HK      999      HJHHKHJHJHKJH8788 
   2      RK      888      OONMNy7878HJHJHSD
   3      SK      777      POMSDHBSNB775SD87
   4      PK      666      HRBMASJMN76448NDN

In Table2 XML column, I want to update the ID node with the new GUID value without changing the element name.

So now the XML would be

   <Root>
    <Data1>
     <ID>HJHHKHJHJHKJH8788</ID>
     <Name>HK</Name> 
     </Data1>
    <Data1>
     <ID>OONMNy7878HJHJHSD</ID>
     <Name>RK</Name>
     </Data1>
    </Root>

This will happen for all rows in Table2 .

Please help me with the query for this.

It is not possible to update the XML in more than one place at a time so you have to do this in a loop of some kind. The best I could come up with was to extract the ID's from the XML in Table2 and join against Table1.ID to produce a temp table that holds Table2.ID ordinal position of the Data1 node in the XML ( OrdPos ) and the new GUID value.

Then you can loop over the max number of nodes present in the XML column and do the update.

-- Variable used to loop over nodes
declare @I int 

-- Temp table to hold the work that needs to be done.
create table #T
(
  ID int, -- ID from table2
  OrdPos int, -- Ordinal position of node Data1 in root
  GUID uniqueidentifier, -- New ID
  primary key (OrdPos, ID)
)

-- Shred the XML in Table2, join to Table1 to get GUID
insert into #T(ID, OrdPos, GUID)
select T2.ID,
       row_number() over(partition by T2.ID order by D.N) as OrdPos,
       T1.GUID
from Table2 as T2
  cross apply T2.XMLCol.nodes('Root[1]/Data1') as D(N)
  inner join Table1 as T1
    on T1.ID = D.N.value('(ID/text())[1]', 'int')

-- Get the max number of nodes in one row that needs to be updated
set @I = 
  (
    select top(1) count(*)
    from #T
    group by ID
    order by 1 desc
  )

-- Do the updates in a loop, one level at a time
while @I > 0
begin
  update T2
  set XMLCol.modify('replace value of (/Root[1]/Data1[sql:variable("@I")]/ID/text())[1] 
                     with sql:column("T.GUID")')
  from Table2 as T2
    inner join #T as T
      on T2.ID = T.ID
  where T.OrdPos = @I

  set @I = @I - 1
end

drop table #T

SQL Fiddle

I got one of them to update.

Close, but no cigar. But it's end of the day.

IF OBJECT_ID('tempdb..#XmlHolderTable') IS NOT NULL
begin
        drop table #XmlHolderTable
end



IF OBJECT_ID('tempdb..#ScalarHolderTable') IS NOT NULL
begin
        drop table #ScalarHolderTable
end



CREATE TABLE #ScalarHolderTable
(
ScalarKey int not null , 
Name varchar(16) , 
Phone varchar(16) , 
UUID uniqueidentifier 
)


CREATE TABLE #XmlHolderTable
(
XmlSurrogateIdentityKey int not null identity (1001, 1), 
TheXml xml
)


INSERT INTO #ScalarHolderTable 
( ScalarKey , Name , Phone , UUID )
            select  1   ,  'HK' ,    999 , NEWID() 
union all   select  2   ,  'RK' ,    888 , NEWID()
union all   select  3   ,  'SK' ,    777 , NEWID()
union all   select  4   ,  'PK' ,    66  , NEWID()


-- Declare XML variable

DECLARE @data XML;

-- Element-centered XML

SET @data = N'


 <Root>
    <Data1>
        <ID>1</ID>
        <Name>HK</Name> 
    </Data1>
    <Data1>
        <ID>2</ID>
        <Name>RK</Name>
    </Data1>
</Root>

';


INSERT INTO #XmlHolderTable ( TheXml) values ( @data )



select TheXml.value('(//Data1/ID)[1]','int') ,  * from #XmlHolderTable


SELECT Data.Col.value('(.)[1]','Int') AS Id
FROM #XmlHolderTable xmlHolder
CROSS APPLY
TheXml.nodes('//Data1/ID') AS Data(Col)

/*
SELECT Data.Col.value('(Id)[1]','Int') AS Id
FROM @Data.nodes('/Root/Data') AS Data(Col)
*/



declare @counter int
select @counter = 0

/*
WHILE ( 
            exists ( select top 1 null
                    From
                        #XmlHolderTable xmlHolder
                    CROSS APPLY
                    TheXml.nodes('//Data1/ID') AS Data(Col) , #ScalarHolderTable scalarHolder
            Where
                ISNUMERIC (     Data.Col.value('(.)[1]','varchar(40)')      ) > 0 

                    )
    )
BEGIN

    select @counter= @counter + 1
    print '/@counter/'
    print @counter
    print ''
*/



UPDATE 
   #XmlHolderTable
SET 
   TheXml.modify('replace value of (//Data1/ID/text())[1] with sql:column("scalarHolder.UUID")') 

--select Data.Col.value('(.)[1]','Int') as MyValue , scalarHolder.ScalarKey

From
    #XmlHolderTable xmlHolder CROSS APPLY TheXml.nodes('//Data1/ID') AS Data(Col) 
    , #ScalarHolderTable scalarHolder
Where
    Data.Col.value('(.)[1]','Int') = scalarHolder.ScalarKey


/*
END
*/



select * from #ScalarHolderTable
select TheXml from #XmlHolderTable




IF OBJECT_ID('tempdb..#XmlHolderTable') IS NOT NULL
begin
        drop table #XmlHolderTable
end



IF OBJECT_ID('tempdb..#ScalarHolderTable') IS NOT NULL
begin
        drop table #ScalarHolderTable
end

Do you absolutely want to modify current xml? because if you can just generate it from your data, it will be much simplier:

update Table2 set
     XMLCol =
     (
         select T1.GUID as ID, T1.Name as Name
         from T2.XMLCol.nodes('Root/Data1') as T(C)
             inner join Table1 as T1 on
                 T1.ID = T.C.value('ID[1]', 'int') and
                 T1.Name = T.C.value('Name[1]', 'varchar(10)')
         for xml path('Data1'), root('Root'), type
     )
from Table2 as T2

see sql fiddle example

update Ok, as far as I understand, each Data1 have only one ID. Then you can do this:

declare @temp table(ID int, T1_ID int, XMLcol xml)

-- split xml, each ID goes in own row    
insert into @temp
select ID, T.C.value('ID[1]', 'int') as ID, T.C.query('.') as XMLCol
from Table2 as T2
    outer apply T2.XMLCol.nodes('Root/Data1') as T(C)

-- modify xml    
update @temp set
    XMLCol.modify('
        replace value of (Data1/ID/text())[1]
        with sql:column("T1.GUID")
    ')
from @temp as T
    inner join Table1 as T1 on T1.ID = T.T1_ID

-- modify original table    
update Table2 set
   XMLCol =
   (
       select (select T.XMLcol)
       from @temp as T
       where T.ID = T2.ID
       for xml path(''), root('Root'), type
   )
from Table2 as T2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM