XML 方法.modify() 允许一次进行一项更改。这意味着每个需要的更改使用一个语句。 CURSOR 或 WHILE 循环可能是个好主意。
由于我试图避免程序逻辑,您可以看看这些替代方案:
分解并重新创建
一种方法是将整个事物切碎并从头开始重新创建:
首先我创建一个模型来模拟你的情况
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 后重新创建 XML:
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 方法
或者,您可以尝试以下方法:
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 表中的值。这将如下所示:
<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 将重建文档,但会根据 <DocAccess> 中对应 ID 元素的存在设置 Status-属性。
最后声明
你可以用一个
- CURSOR 每次更改都有单独的 statemets,
- 您可以分解并重新创建 XML 或
- 您可以使用 XQuery/FLWOR 重新构建 XML。
这取决于您的需求,您喜欢哪种方法。