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 и воссоздать XML после использования CASE
для установки необходимых status
значений на 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 подход
В качестве альтернативы вы можете попробовать что-то подобное:
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 перестроит документ, но установит атрибут Status
в зависимости от наличия соответствующего элемента ID в <DocAccess>
.
Окончательное утверждение
Вы можете использовать
- CURSOR с отдельными состояниями на изменение,
- вы можете уничтожить и заново создать XML или
- вы можете использовать XQuery / FLWOR для перестройки XML.
Это зависит от ваших потребностей, какой подход вы предпочитаете.