需要使用来自 SQL 查询的值更新 XML 字符串中的多个节点 [英] Need to update multiple nodes in a XML string using values from an SQL query

查看:36
本文介绍了需要使用来自 SQL 查询的值更新 XML 字符串中的多个节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个SQL存储过程局部变量@DocList(声明@DocList XML)其中包含以下 XML 数据:

I have a SQL stored procedure local variable @DocList (Declare @DocList XML) which contains the follwing XML data:

<JobList ListItems="7">
  <Job JobFriendlyName="EMAIL INVOICES">
    <DocumentList>
      <Document Doc="1" ID="5280301.2019050148902.00020" Date="05-03-2019" Status="NEW" />
      <Document Doc="2" ID="5280301.2019050148902.00022" Date="05-03-2019" Status="NEW" />
      <Document Doc="3" ID="5280301.2019050148902.00023" Date="05-03-2019" Status="NEW" />
      <Document Doc="4" ID="5280301.2019050104301.00055" Date="05-02-2019" Status="NEW" />
      <Document Doc="5" ID="5280301.2019050104301.00056" Date="05-02-2019" Status="NEW" />
    </DocumentList>
  </Job>
  <Job JobFriendlyName="INVOICES">
    <DocumentList>
      <Document Doc="6" ID="5280300.2019050148901.00001" Date="05-03-2019" Status="NEW" />
      <Document Doc="7" ID="5280300.2019050148901.00002" Date="05-03-2019" Status="NEW" />
    </DocumentList>
  </Job>
</JobList>

我还有一个 SQL 表DocAccess",其中包含 0 行或更多行,DocIDNumber 与 XML 中匹配的ID"属性值相关:

I also have an SQL table "DocAccess" which contains 0 or more rows with DocIDNumber the correlate to matching "ID" attribute values in the XML:

TABLE [tblDocAccess]
(
    [Key] varachar(10),
    [DocIDNumber] [varchar](35),
    [DocLastOpenDtg] [smalldatetime]
)

我想应用查询select [DocIDNumber] from [tblDocAccess] where [Key] = {some任意值}"针对@DocList 中的 XML,将每个节点的属性Status"值从NEW"修改为OLD"属性ID"值匹配返回的 [DocIdNumber] 值.

I want to apply query "select [DocIDNumber] from [tblDocAccess] where [Key] = {some arbitrary value}" against the XML in @DocList to modify attribute "Status" value from "NEW" to "OLD" for each node where attribute "ID" values matches a returned [DocIdNumber] values.

我知道我可以为 select 语句创建一个游标,然后循环定位/更新任何匹配节点/属性值,但这确实说明似乎有效.

I know I could create a cursor fror the select statement, then loop to locate/update any matching node/attribute value, but that does note seem efficient.

任何建议的帮助将不胜感激.

Any assistance of suggestions would be appreciated.

====================================

===================================

后续问题:使用上面在@DocList 中显示的 XML 文档和另一个包含要搜索的值的局部变量 @SearchID varchar(35),我将如何编码所需的 {While ... Exists ... 设置) 逻辑将 ID 与 @SearchID 中的值匹配的文档的状态设置为OLD".

Follow-up question: Using the XML document shown above in @DocList, and another local variable, @SearchID varchar(35) which contains a value to search for, how would I code the required {While ... Exists ... Set) logic to set Status to "OLD" for the Document with ID matching the value in @SearchID.

请原谅我的无知.我使用 SQL 已经很多年了,但这是我第一次尝试更新现有的 XML 文档.

Please forgive my ignorance here. I have been working with SQL for many years, but this is my first attempt to update an existing XML document.

推荐答案

XML 方法 .modify() 允许一次进行一个更改.这意味着对每个需要的更改使用一个语句.CURSORWHILE 循环可能是个好主意.

The XML-method .modify() allows for one change at a time. This would mean to use one statement per needed change. A CURSOR or a WHILE loop might be a good idea.

当我尽量避免程序逻辑时,你可以看看这些替代方案:

As I try to avoid procedural logic, you can have a look at these alternatives:

一种方法是将整个内容切碎并从头开始重新创建:

One approach was to shred the whole thing and recreate it from scratch:

首先我创建一个模型来模拟你的情况

First I create a mockup to simulate your situation

DECLARE @DocList XML=
N'<JobList ListItems="7">
  <Job JobFriendlyName="EMAIL INVOICES">
    <DocumentList>
      <Document Doc="1" ID="5280301.2019050148902.00020" Date="05-03-2019" Status="NEW" />
      <Document Doc="2" ID="5280301.2019050148902.00022" Date="05-03-2019" Status="NEW" />
      <Document Doc="3" ID="5280301.2019050148902.00023" Date="05-03-2019" Status="NEW" />
      <Document Doc="4" ID="5280301.2019050104301.00055" Date="05-02-2019" Status="NEW" />
      <Document Doc="5" ID="5280301.2019050104301.00056" Date="05-02-2019" Status="NEW" />
    </DocumentList>
  </Job>
  <Job JobFriendlyName="INVOICES">
    <DocumentList>
      <Document Doc="6" ID="5280300.2019050148901.00001" Date="05-03-2019" Status="NEW" />
      <Document Doc="7" ID="5280300.2019050148901.00002" Date="05-03-2019" Status="NEW" />
    </DocumentList>
  </Job>
</JobList>';

DECLARE @mockupDocAccess TABLE
(
    [Key] varchar(10),
    [DocIDNumber] [varchar](35),
    [DocLastOpenDtg] [smalldatetime]
);

INSERT INTO @mockupDocAccess VALUES('SomeKey','5280301.2019050148902.00022',GETDATE())   --Doc 2
                                  ,('SomeKey','5280301.2019050104301.00055',GETDATE())   --Doc 4
                                  ,('SomeKey','5280300.2019050148901.00001',GETDATE())   --Doc 6
                                  ,('OtherKey','5280301.2019050104301.00056',GETDATE()); --Doc 5

--现在我们可以从 XML 中读取所有值并在使用 CASE 将所需的 status 值设置为 OLD:

--Now we can read all values from the XML and re-create the XML after using CASE to set the needed status values to OLD:

DECLARE @Key VARCHAR(10)='SomeKey';

WITH AllEmailInvoices AS
(
    SELECT d.value('@Doc','int') AS Doc
          ,d.value('@ID','nvarchar(35)') AS ID
          ,d.value('@Date','nvarchar(10)') AS [Date] --unconverted
          ,CASE WHEN EXISTS(SELECT 1 FROM @mockupDocAccess da WHERE da.DocIDNumber=d.value('@ID','nvarchar(35)') AND da.[Key]=@Key) THEN 'OLD' ELSE d.value('@Status','nvarchar(10)') END AS [Status]
 
    FROM @DocList.nodes('/JobList/Job[@JobFriendlyName="EMAIL INVOICES"]/DocumentList/Document') A(d)
)
,AllInvoices AS
(
    SELECT d.value('@Doc','int') AS Doc
          ,d.value('@ID','nvarchar(35)') AS ID
          ,d.value('@Date','nvarchar(10)') AS [Date] --unconverted
          ,CASE WHEN EXISTS(SELECT 1 FROM @mockupDocAccess da WHERE da.DocIDNumber=d.value('@ID','nvarchar(35)') AND da.[Key]=@Key) THEN 'OLD' ELSE d.value('@Status','nvarchar(10)') END AS [Status]
 
    FROM @DocList.nodes('/JobList/Job[@JobFriendlyName="INVOICES"]/DocumentList/Document') A(d)
)
SELECT @DocList.value('(/JobList/@ListItems)[1]','int') AS [@ListItems]
      ,(
            SELECT 'EMAIL INVOICES' AS [@JobFriendlyName]
                   ,(
                        SELECT Doc AS [@Doc]
                              ,ID AS [@ID]
                              ,[Date] AS [@Date]
                              ,[Status] AS [@Status]      
                        FROM AllEmailInvoices
                        FOR XML PATH('Document'),ROOT('DocumentList'),TYPE
                    )
            FOR XML PATH('Job'),TYPE
        )
        ,(
            SELECT 'INVOICES' AS [@JobFriendlyName]
                   ,(
                        SELECT Doc AS [@Doc]
                              ,ID AS [@ID]
                              ,[Date] AS [@Date]
                              ,[Status] AS [@Status]      
                        FROM AllInvoices
                        FOR XML PATH('Document'),ROOT('DocumentList'),TYPE
                    )
            FOR XML PATH('Job'),TYPE
        )
FOR XML PATH('JobList');

XQuery 和 FLWOR 方法

或者,您可以尝试以下方法:

XQuery and FLWOR approach

Alternatively you can try something along this:

DECLARE @Key VARCHAR(10)='SomeKey';

SELECT
(
    SELECT (SELECT DocIDNumber AS ID FROM @mockupDocAccess WHERE [Key]=@Key FOR XML PATH(''),TYPE) DocAccess
          ,@DocList
    FOR XML PATH(''),TYPE
).query
(N'
    <JobList> {/JobList/@*}
    {
    for $j in /JobList/Job 
    return
        <Job> {$j/@*}
        {
            <DocumentList>
            {
            for $d in $j/DocumentList/Document
            return
                <Document Doc="{$d/@Doc}" 
                          ID="{$d/@ID}" 
                          Date="{$d/@Date}" 
                          Status="{if(/DocAccess[ID=$d/@ID]) then "OLD" else xs:string($d/@Status)}" />
            }
            </DocumentList>
        }
        </Job>
    }
    </JobList>
');

首先,我们创建一个 XML,其中包含来自 DocAccess 表的值.这将如下所示:

First we create a XML where we include the values from the DocAccess-table. This will look like this:

<DocAccess>
  <ID>5280301.2019050148902.00022</ID>
  <ID>5280301.2019050104301.00055</ID>
  <ID>5280300.2019050148901.00001</ID>
</DocAccess>
<JobList ListItems="7">
  <!-- Your Content here -->
</JobList>

XQuery 将重建文档,但将根据 中相应 ID 元素的存在情况设置 Status 属性.

The XQuery will rebuild the document but will set the Status-attribute depending on the existance of a corresponding ID element in <DocAccess>.

你可以使用一个

  • 每次更改都有单独的语句的 CURSOR,
  • 您可以粉碎并重新创建 XML 或
  • 您可以使用 XQuery/FLWOR 重新构建 XML.

这取决于您的需求,您喜欢哪种方法.

It depends on your needs, which approach you prefer.

这篇关于需要使用来自 SQL 查询的值更新 XML 字符串中的多个节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆