Search code examples
xmlsql-server-2005xml-dml

Deleting multiple nodes in SQL Server XML field


If you run this script in SQL Server 2005:

create table #xmltemp
(
    id int,
    data xml null
)

insert into #xmltemp
select 1, ''

update #xmltemp
set data.modify('insert <a id="1" /> into /')
update #xmltemp
set data.modify('insert <a id="2" /> into /')
update #xmltemp
set data.modify('insert <a id="3" /> into /')
update #xmltemp
set data.modify('insert <a id="4" /> into /')

select * from #xmltemp

update x
set data.modify('delete //a[@id=sql:column("t.xmlid")]')
from #xmltemp x
inner join (
    select 1 as id, 1 as xmlid union
    select 1 as id, 2 as xmlid union
    select 1 as id, 3 as xmlid
) t on t.id = x.id

select * from #xmltemp

drop table #xmltemp

You will get the following output:

id  data
1   <a id="1" /><a id="2" /><a id="3" /><a id="4" />

id  data
1   <a id="2" /><a id="3" /><a id="4" />

I would expect it to delete all three nodes rather than just the first, making the second select return:

id  data
1   <a id="4" />

Is there a way to delete multiple nodes in a single query? Specifically, I want to delete all nodes that match criteria from a column in another table (in this example t is created on the fly but it could just as easily have been an actual table).


Solution

  • You can make xmlid from the joined query into a comma separated string and use that string in the predicate.

    create table #IdToDelete(id int, xmlid int)
    insert into #IdToDelete values (1, 1)
    insert into #IdToDelete values (1, 2)
    insert into #IdToDelete values (1, 3)
    insert into #IdToDelete values (2, 4)
    
    update x
    set data.modify('delete //a[contains(sql:column("t.xmlid"), @id)]')
    from #xmltemp x
    inner join (
        select D1.id,
               (select ','+cast(D2.xmlid as varchar(10))
                from #IdToDelete as D2
                where D1.id = D2.id
                for xml path('')) as xmlid
        from #IdToDelete as D1
        group by D1.id
    ) t on t.id = x.id