Efficient way of doing “insert only if node with specific value does not exist” for an xml column
-
02-07-2021 - |
Domanda
Say, I have a xml column that looks like this:
<mi>
<m>42</m>
</mi>
Assuming table:
Word(WordId:bigint, Wordtext:nvarchar, MessageIndex:xml)
I do NOT want the following parameterized query to insert a new xml node if @MessageId already exists somewhere in the xml tree of Messageindex, but rather either fail with a deterministic error code, or silently:
begin try
insert into Word (WordText, MessageIndex) values (@WordText, '<mi></mi>');
update Word set MessageIndex.modify('insert (<m>{sql:variable(""@MessageId"")}</m>) into(/mi)[1]') where WordId = scope_identity();
end try
begin catch
if error_number() = 2627
begin
update Word set
MessageIndex.modify('insert (<m>{sql:variable(""@MessageId"")}</m>) into(/mi)[1]')
where
WordText = @WordText;
end
else
throw
end catch
select WordId from Word where WordText = @WordText;
How do I make this happen efficiently?
Soluzione
Something like this work for you?
DECLARE @Word TABLE (WordId bigint identity, Wordtext NVARCHAR(20), MessageIndex xml )
insert into @word ( messageIndex )
select '<mi>
<m>42</m>
</mi>'
DECLARE @WordText NVARCHAR(20) = 'wordText'
DECLARE @messageId INT = 42
begin try
if exists ( select 1 from @Word where MessageIndex.exist('//mi[.=sql:variable("@MessageId")]') = 0 )
begin
insert into @Word (WordText, MessageIndex) values (@WordText, '<mi></mi>');
update @Word set MessageIndex.modify('insert <m>{sql:variable("@MessageId")}</m> into (mi)[1]')
where WordId = scope_identity();
end
--else
-- do something here?
end try
begin catch
if error_number() = 2627
begin
update @Word set MessageIndex.modify('insert <m>{sql:variable("@MessageId")}</m> into (mi)[1]')
where WordText = @WordText;
end
else
throw
end catch
select * from @Word
Altri suggerimenti
This is an older post, but I'd to share my solution, which applies when an element either exists or not:
First, we need update when element (varchar type) exists:
UPDATE PD
SET
[ValuesXml].modify('replace value of
(/Employee/KeyValue/text())[1] with (sql:column("x.KeyValue"))')
FROM Pdetail PD
INNER JOIN TableXml x ON PD.PId = x.PId
WHERE PD.[ValuesXml].exist('/Employee/KeyValue') = 1
Second, we need insert when element does not exists:
UPDATE PD
SET
[ValuesXml].modify(
'insert <KeyValue>{sql:column("x.KeyValue")}</KeyValue> into (/Employee)[1]')
FROM RK.Pdetail PD
INNER JOIN TableXml x ON PD.PId = x.PId
WHERE PD.[ValuesXml].exist('/Employee/KeyValue') = 0
If you need work with multiple instances of entities in XML you'll need use CROSS APPLY and remove the [1] singleton indicator.