【问题标题】:SQL Server Update Value XML NodeSQL Server 更新值 XML 节点
【发布时间】:2013-11-20 17:55:30
【问题描述】:

我在 SQL Server 中有 2 个表

表 1

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

表2

   ID   - XMLCol
   1      XMLVal1

XMLVal1

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

现在我在 Table1

中插入一个 GUID 列

表1

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

Table2 XML 列中,我想用新的 GUID 值更新ID 节点而不更改元素名称。

所以现在 XML 将是

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

Table2 中的所有行都会发生这种情况。

请帮我解决这个问题。

【问题讨论】:

  • marc_s,我知道如何根据条件选择 xml 值,但不知道如何更新属于另一个表的值。
  • 基本上是这样的:Update table2 set XMLCol.Modify('Root/Data1/ID') = (Select GUID From Table1 Where ID = XMLCol.Query('Root/Data1/ID'))你能帮我准确查询吗?
  • table2 可以多行吗? table1 中的 id 可以在 XML 中为 table2 中的一行重复使用一次以上吗?同一个id可以在table2中多行重复使用吗?
  • 是的,table2 中有超过 1 行。是的,ID 列可以在 XML 中多次使用,但在不同的节点块中。块是指 Data1 节点和其中的元素。
  • 可以,但是同一个id value可以重复使用吗?

标签: sql sql-server xml sqlxml


【解决方案1】:

不可能一次在多个地方更新 XML,因此您必须在某种循环中执行此操作。我能想到的最好办法是从Table2 中的XML 中提取ID 并加入Table1.ID 以生成一个临时表,其中包含Data1 节点在XML 中的Table2.ID 序号位置(OrdPos ) 和新的GUID 值。

然后您可以遍历 XML 列中存在的最大节点数并进行更新。

-- 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

【讨论】:

    【解决方案2】:

    我要更新其中一个。

    关闭,但没有雪茄。但这一天结束了。

    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
    

    【讨论】:

    • 感谢granadaCoder,但正如你所说,它刚刚更新了xml中的1个值,其他块也应该更新。我正在尝试使用您的方法来完成此操作。如果我成功将发布它。是的 Mikael Eriksson,该值可以重复。例如。 ** 2China1USA1印度**
    【解决方案3】:

    您确定要修改当前的 xml 吗?因为如果你可以从你的数据中生成它,它会简单得多:

    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
    

    参见sql fiddle 示例

    update好的,据我了解,每个Data1只有一个ID。然后你可以这样做:

    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
    

    【讨论】:

    • 罗马感谢您的回复。我的 XML 大约有 300-400 行,为了便于理解,我制作了这个更简单的 xml,试图复制我需要的行为。
    • 更新了另一种可能的解决方案(如果您只有每个 Data1 的 ID)
    猜你喜欢
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-19
    相关资源
    最近更新 更多