【问题标题】:Use SQL Server modify('insert') to append data to xml column使用 SQL Server modify('insert') 将数据附加到 xml 列
【发布时间】:2016-11-09 20:46:16
【问题描述】:

考虑以下情况。我有下表

CREATE TABLE [dbo].[GoldenEgg]
(       
    rowIndex int NOT NULL IDENTITY(1,1),    
    AccountNumber varchar(256) NULL,            
    SubscriptionID int NOT NULL,            
    SubscriptionData_XML xml NULL,
    SubscriptionData_AFTER_XML NULL     

    CONSTRAINT [PK_GoldenEgg] 
        PRIMARY KEY CLUSTERED ([rowIndex] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GoldenEgg 样本数据:

SubscriptionData_XML SubscriptionID 6070 的数据:

<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Item>
    <Name>AccountNumbers</Name>
    <Value>
      <ValueItem>39448474</ValueItem>     
    </Value>
  </Item>
</NVPList>

我想将每个 SubscriptionID 的所有帐号附加到 SubscriptionData_XML 列中已经存在的 xml &lt;Value&gt; 节点,并且我不想添加 xml 中已经存在的帐号。

因此对于 SubscriptionID 6070 帐号 39448474 应该只在 xml 中列出一次,如下所示:

<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Item>
    <Name>AccountNumbers</Name>
    <Value>
      <ValueItem>39448474</ValueItem>
      <ValueItem>56936495</ValueItem>
      <ValueItem>70660044</ValueItem>
      <ValueItem>41447395</ValueItem>    
    </Value>
  </Item>
</NVPList>

【问题讨论】:

    标签: sql sql-server xml xml-namespaces xml-dml


    【解决方案1】:

    如果您的 XML 中没有其他节点,您可以选择 FLWOR-query

    一些提示:

    • 首先我创建一个模型表并用数据填充它
    • 我使用和可更新的 CTE 来收集数据
    • 我使用FOR XML-sub-select 没有命名空间来构建&lt;Value&gt; 节点,而不用担心实际 XML 中已经存在的 ID
    • 我使用 FLWOR-query() 从刚刚创建的值节点中构建完整的 XML
    • 由于这个 CTE 是可更新的,我可以直接将它用于UPDATE
    • 最后的SELECT * FROM @tbl 向您显示,所有AFTER_XML 都已填满

    试试这个:

    DECLARE @tbl TABLE(rowIndex INT IDENTITY,AccountNumber INT,SubscriptionID INT, SubscriptionData_XML XML,SubscriptionData_AFTER_XML XML);
    INSERT INTO @tbl VALUES
     (1111,6070,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Item>
        <Name>AccountNumbers</Name>
        <Value>
          <ValueItem>39448474</ValueItem>     
        </Value>
      </Item>
    </NVPList>',NULL)
    ,(2222,6070,NULL,NULL)
    ,(3333,6070,NULL,NULL)
    ,(4444,6070,NULL,NULL)
    ,(5555,6071,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Item>
        <Name>AccountNumbers</Name>
        <Value>
          <ValueItem>39448474</ValueItem>     
        </Value>
      </Item>
    </NVPList>',NULL)
    ,(6666,6071,NULL,NULL)
    ,(7777,6071,NULL,NULL)
    ,(8888,6071,NULL,NULL);
    

    --这里开始可更新的CTE

    WITH UpdateableCTE AS
    (
        SELECT t1.rowIndex
              ,t1.SubscriptionData_AFTER_XML
              ,(
                SELECT t2.AccountNumber AS ValueItem
                FROM @tbl AS t2
                WHERE t2.SubscriptionID=t1.SubscriptionID
                FOR XML PATH(''),ROOT('Value'),TYPE
               ).query
                    (N'declare default element namespace "http://www.whatevernamspace.com/v1";
                       let $nd:=/*:Value
                       return
                       <NVPList>
                           <Item>
                              <Name>{sql:column("XmlName")}</Name>
                              <Value>
                               {
                               for $vi in $nd/*:ValueItem
                               return <ValueItem>{$vi/text()}</ValueItem>
                               }
                              </Value>
                           </Item>
                       </NVPList>
                      '
                    ) AS NewXML
    
        FROM @tbl AS t1
        CROSS APPLY( SELECT t1.SubscriptionData_XML.value('(//*:Name)[1]','nvarchar(max)') AS XmlName) AS x
        WHERE SubscriptionData_XML IS NOT NULL
    )
    

    --UPDATE 语句

    UPDATE UpdateableCTE SET SubscriptionData_AFTER_XML=NewXML
    FROM UpdateableCTE;
    

    --检查成功的SELECT

    SELECT * FROM @tbl
    

    【讨论】:

    • 我写这篇文章是为了接受我自己的答案,因为这是我已经实施的解决方案。您必须等待 48 小时才能在 stackoverflow 上执行此操作。您的解决方案很棒,同样使用您的解决方案,您基本上只是用帐号 (1111,2222,3333,4444) 重写 xml,这将替换现有的 39448474 值。我想保留所有原始 ValueItem 的
    • @iambdot 没关系!接受您最喜欢的答案是您的责任……祝您编码愉快!
    【解决方案2】:

    我能够通过使用 xml modify() 方法的 sql UPDATE 语句完成此任务,而无需使用任何循环。以下是解决方案的细分:

    1) 我必须获取 SubscriptionID 的所有 AccountNumbers 并将它们格式化为 进入 xml &lt;ValueItem&gt; 节点。

    SQL 查询 1:

    SELECT 
    ge.SubscriptionID,
    CAST((SELECT DISTINCT ValueItem = ISNULL(ge2.AccountNumber,'')
            FROM dbo.GoldenEgg ge2
            WHERE ge2.SubscriptionID = ge.SubscriptionID                        
            FOR XML PATH('')) AS xml) AS AccountNumberXml
    FROM dbo.GoldenEgg ge
    WHERE ge.SubscriptionData_XML IS NOT NULL
    

    SQL 查询 1 结果:

    SQL 查询 1 XML 结果(订阅 ID 6070):

    <ValueItem>39448474</ValueItem>
    <ValueItem>41447395</ValueItem>
    <ValueItem>56936495</ValueItem>
    <ValueItem>70660044</ValueItem>
    


    2) 现在我在单个值中拥有 AccountNumbers,我现在可以使用 xml modify() 方法并将 AccountNumberXml 值插入到 &lt;Value&gt; xml 节点的最后一个位置。我将使用带有INNER JOINUPDATE 语句来执行此操作。另请注意,在执行任何操作之前,我最初将 SubscriptionData_AFTER_XML 设置为等于 SubscriptionData_XML。

    SQL 查询 2:

    UPDATE ge
        SET SubscriptionData_AFTER_XML.modify
        ('declare default element namespace "http://www.whatevernamspace.com/v1";
          insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')          
        FROM dbo.GoldenEgg ge
        INNER JOIN (SELECT 
                    ge2.SubscriptionID,
                    CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
                            FROM dbo.GoldenEgg ge1                                              
                            WHERE ge1.SubscriptionID = ge2.SubscriptionID                       
                            FOR XML PATH('')) AS xml) as AccountNumberXml
                    FROM dbo.GoldenEgg ge2
                    WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
        WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
    

    SQL 查询 2 结果:

    SQL QUERY 2 XML RESULT(SubscriptionID 6070 SubscriptionData_AFTER_XML 列):

    <NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Item>
        <Name>AccountNumbers</Name>
        <Value>
          <ValueItem>39448474</ValueItem>
          <ValueItem xmlns="">39448474</ValueItem>
          <ValueItem xmlns="">41447395</ValueItem>
          <ValueItem xmlns="">56936495</ValueItem>
          <ValueItem xmlns="">70660044</ValueItem>
        </Value>
      </Item>
    </NVPList> 
    



    如您所见,SubscriptionData_AFTER_XML 列中的最终 xml 结果现在存在两个问题。

    问题 1

    对于subscriptionID 6070 AccountNumber 39448474 在&lt;ValueItem&gt; 节点列表中重复,这是我不想要的。为了解决这个问题,我必须查询 xml 中的当前 AccountNumber 值,并从以前的 INNER JOIN 中排除这些 AccountNumbers

    SQL 查询 3:
    此查询将为我提供一个包含 SubscriptionData_XML 列中所有当前 AccountNumbers 的结果集,然后我可以使用它从 SQL QUERY 1 结果集中排除这些 AccountNumbers

    SELECT SubscriptionID, t.c.value('.', 'varchar(MAX)') as CurrentValueItems
    FROM dbo.GoldenEgg 
    CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
                                        /NVPList/Item/Value/ValueItem') as t(c)
    WHERE SubscriptionData_XML IS NOT NULL
    

    SQL 查询 3 结果:

    现在将它们放在一起以获得正确的最终结果

    SQL 查询 4:

    UPDATE ge
    SET SubscriptionData_AFTER_XML.modify
    ('declare default element namespace "http://www.whatevernamspace.com/v1";
      insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')          
    FROM dbo.GoldenEgg ge
    INNER JOIN (SELECT 
                ge2.SubscriptionID,
                CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
                        FROM dbo.GoldenEgg ge1
                        --make sure we are not inserting AccountNumbers that already exists in the subscription data
                        WHERE ge1.AccountNumber NOT IN (SELECT t.c.value('.', 'varchar(MAX)') as CurrentValueItems
                                                        FROM dbo.GoldenEgg 
                                                        CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
                                                                                         /NVPList/Item/Value/ValueItem') as t(c)
                                                        WHERE SubscriptionData_XML IS NOT NULL
                                                        AND SubscriptionID = ge2.SubscriptionID) 
                        AND ge1.SubscriptionID = ge2.SubscriptionID                     
                        FOR XML PATH('')) AS xml) as AccountNumberXml
                FROM dbo.GoldenEgg ge2
                WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
    WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
    

    SQL QUERY 4 XML RESULT(SubscriptionID 6070 SubscriptionData_AFTER_XML 列):

    如您所见,AccountNumber 39448474 现在只在 xml 中列出一次

    <NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Item>
        <Name>AccountNumbers</Name>
        <Value>
          <ValueItem>39448474</ValueItem>
          <ValueItem xmlns="">41447395</ValueItem>
          <ValueItem xmlns="">56936495</ValueItem>
          <ValueItem xmlns="">70660044</ValueItem>
        </Value>
      </Item>
    </NVPList>
    



    问题 2

    当插入带有AccountNumber 的节点列表时,插入的是一个空的xmlns="" 命名空间。这是我用来删除空的 xmlns="" 命名空间的查询。

    SQL 查询 5:

    UPDATE dbo.GoldenEgg
    SET SubscriptionData_AFTER_XML = CONVERT(XML, REPLACE(CONVERT(NVARCHAR(MAX), SubscriptionData_AFTER_XML), N'xmlns=""',''))
    WHERE SubscriptionData_AFTER_XML IS NOT NULL
    

    SQL 查询 5 XML 结果(订阅 ID 6070):

    <NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Item>
        <Name>AccountNumbers</Name>
        <Value>
          <ValueItem>39448474</ValueItem>
          <ValueItem>41447395</ValueItem>
          <ValueItem>56936495</ValueItem>
          <ValueItem>70660044</ValueItem>
        </Value>
      </Item>
    </NVPList>
    


    我希望这可以帮助任何可能需要做类似事情的人

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    相关资源
    最近更新 更多