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:
Shred and re-create
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
--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 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>
');
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>
The XQuery will rebuild the document but will set the Status-attribute depending on the existance of a corresponding ID element in <DocAccess>.
Final statement
You can use a
- CURSOR with separate statemets per change,
- you can shred and re-create the XML or
- you can use XQuery/FLWOR to re-build the XML.
It depends on your needs, which approach you prefer.