简体   繁体   English

使用SQL Server修改('insert')将数据附加到xml列

[英]Use SQL Server modify('insert') to append data to xml column

Consider the following situation. 考虑以下情况。 I have the following table 我有下表

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 sample data: GoldenEgg样本数据:

GoldenEgg表

SubscriptionData_XML data for SubscriptionID 6070: SubscriptionID 6070的SubscriptionData_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>     
    </Value>
  </Item>
</NVPList>

I want to append all account numbers for each SubscriptionID to the already existing xml <Value> node in the SubscriptionData_XML column and I do not want to add account numbers that already exist in the xml. 我想将每个SubscriptionID的所有帐号附加到SubscriptionData_XML列中已存在的xml <Value>节点,我不想添加xml中已存在的帐号。

So for SubscriptionID 6070 account number 39448474 should only be listed once in the xml like so: 因此,对于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>

If there are not other nodes within your XML you might choose the FLWOR-query . 如果XML中没有其他节点,则可以选择FLWOR查询

Some hints: 一些提示:

  • first I create a mock-up table and fill it with data 首先,我创建一个模型表并用数据填充它
  • I use and updateable CTE to collect the data 我使用和更新CTE来收集数据
  • I use a FOR XML -sub-select without a namespace to build the <Value> node wihtout bothering about already existing IDs in your actual XML 我使用没有命名空间FOR XML -sub-select来构建<Value>节点,而不用担心实际XML中已存在的ID
  • I use a FLWOR-query() to build up the full XML out of the just created Value-node 我使用FLWOR-query()从刚刚创建的Value-node构建完整的XML
  • As this CTE is updateable, I can use it directly for the UPDATE 由于此CTE是可更新的,我可以直接将其用于UPDATE
  • The final SELECT * FROM @tbl shows to you, that all AFTER_XML are filled 最终的SELECT * FROM @tbl向您显示所有AFTER_XML都已填充

Try this: 尝试这个:

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

--Here starts the updateable CTE - 这是启动可更新的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
)

--The UPDATE statement - UPDATE语句

UPDATE UpdateableCTE SET SubscriptionData_AFTER_XML=NewXML
FROM UpdateableCTE;

--The SELECT to check the success - 用于检查成功的SELECT

SELECT * FROM @tbl

I was able to accomplish this task with a sql UPDATE statement using the xml modify() method and without using any loops. 我能够使用xml modify()方法使用sql UPDATE语句完成此任务,而不使用任何循环。 Here is a breakdown of the solution: 以下是解决方案的细分:

1) I had to get all the AccountNumbers for the SubscriptionID and format them in into xml <ValueItem> nodes. 1)我必须得到SubscriptionID的所有AccountNumbers并将它们格式化为xml <ValueItem>节点。

SQL QUERY 1: SQL QUERY 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 QUERY 1 RESULT: SQL QUERY 1结果:
AccountNumberXml

SQL QUERY 1 XML RESULT (SubscriptionID 6070): SQL QUERY 1 XML RESULT (SubscriptionID 6070):

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


2) Now that I have the AccountNumbers in a single value, I can now use the xml modify() method and insert the AccountNumberXml value into the last position of the <Value> xml node. 2)现在我将AccountNumbers放在一个值中,现在我可以使用xml modify()方法并将AccountNumberXml值插入<Value> xml节点的最后一个位置。 I will do this using an UPDATE statement with INNER JOIN . 我将使用INNER JOINUPDATE语句执行此操作。 Also note that I initally set SubscriptionData_AFTER_XML equal to SubscriptionData_XML before doing anything. 另请注意,在执行任何操作之前,我最初将SubscriptionData_AFTER_XML设置为等于SubscriptionData_XML。

SQL QUERY 2: SQL QUERY 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 QUERY 2 RESULT: SQL QUERY 2结果: 在此输入图像描述

SQL QUERY 2 XML RESULT (SubscriptionID 6070 SubscriptionData_AFTER_XML column): 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> 



As you may see there are now two problems with the final xml result in the SubscriptionData_AFTER_XML column. 正如您所看到的,SubscriptionData_AFTER_XML列中的最终xml结果现在存在两个问题。

Problem 1 问题1

For subscriptionID 6070 AccountNumber 39448474 is being repeated in the <ValueItem> node list, which I do not want. 对于subscriptionID 6070,在<ValueItem>节点列表中重复了AccountNumber 39448474,这是我不想要的。 To fix this I have to query the current AccountNumber values in the xml and exclude those AccountNumbers from the previous INNER JOIN 要修复此问题,我必须查询xml中的当前AccountNumber值,并从之前的INNER JOIN排除那些AccountNumbers

SQL QUERY 3: SQL QUERY 3:
This query will give me a result set with all the current AccountNumbers in the SubscriptionData_XML column, which I can then use to exclude these AccountNumbers from the SQL QUERY 1 result set 此查询将为我提供一个结果集,其中包含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 QUERY 3 RESULT: SQL QUERY 3结果:
CurrentValueItems

Now putting it all together to get the correct final result 现在将它们放在一起以获得正确的最终结果

SQL QUERY 4: SQL QUERY 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 column): SQL QUERY 4 XML RESULT (SubscriptionID 6070 SubscriptionData_AFTER_XML列):

As you can see AccountNumber 39448474 is now only listed once in the 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>



Problem 2 问题2

When the with AccountNumber node list is inserted, it is being inserted with an empty xmlns="" namespace. 插入带有AccountNumber节点列表时,将插入一个空的xmlns=""命名空间。 This is query I used to remove the empty xmlns="" namespace. 这是我用来删除空xmlns=""命名空间的查询。

SQL QUERY 5: SQL QUERY 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 QUERY 5 XML RESULT (SubscriptionID 6070): SQL QUERY 5 XML RESULT (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>
      <ValueItem>41447395</ValueItem>
      <ValueItem>56936495</ValueItem>
      <ValueItem>70660044</ValueItem>
    </Value>
  </Item>
</NVPList>


I hope this helps anyone who may need to do something similar 我希望这可以帮助任何可能需要做类似事情的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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