2

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?

Yuriy Gettya
  • 693
  • 10
  • 20

2 Answers2

3

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
wBob
  • 13,710
  • 3
  • 20
  • 37
  • Thanks! I can use that. Not exactly what I wanted, but that's my fault - didn't specify that I wanted to avoid duplicates in the catch clause only. – Yuriy Gettya Sep 26 '12 at 23:39
2

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.

Fer R
  • 141
  • 2
  • 9