2009-06-19 65 views
5

我:删除多个节点的单次XQuery的SQL Server

  1. XML类型列的表(ID列表)
  2. XML类型参数(也ID列表)

什么是从列中删除节点匹配参数中的节点,同时保持任何不匹配的节点不变的最佳方式?

例如

declare @table table (
    [column] xml 
) 

insert @table ([column]) values ('<r><i>1</i><i>2</i><i>3</i></r>') 

declare @parameter xml 
set @parameter = '<r><i>1</i><i>2</i></r>' 

-- this is the problem 
update @table set [column].modify('delete (//i *where text() matches @parameter*)') 

MSDN文档表明它应该是可能的(在Introduction to XQuery in SQL Server 2005):

该存储过程可以很容易地 修改以接受其含有一个或多个技术人员 元件从而允许的XML片段 用户到 用一个 单一调用存储过程来删除多个技能节点。

回答

3

虽然这样做的删除有点尴尬,但如果数据很简单(例如您提供的示例),则可以改为更新数据。以下查询将基本上将两个XML字符串拆分为表,加入它们,排除非空(匹配)值并将其转换回XML:

UPDATE @table 
SET [column] = (
    SELECT p.i.value('.','int') AS c 
    FROM [column].nodes('//i') AS p(i) 
    OUTER APPLY (
     SELECT x.i.value('.','bigint') AS i 
     FROM @parameter.nodes('//i') AS x(i) 
     WHERE p.i.value('.','bigint') = x.i.value('.','int') 
    ) a 
    WHERE a.i IS NULL 
    FOR XML PATH(''), TYPE 
) 
3

你需要的东西形式:

[column].modify('delete (//i[.=(1, 2)])') -- like SQL IN 
-- or 
[column].modify('delete (//i[.=1 or .=2])') 
-- or 
[column].modify('delete (//i[.=1], //i[.=2])') 
-- or 
[column].modify('delete (//i[contains("|1|2|",concat("|",.,"|"))])') 

XQuery不支持XML SQL类型SQL2005和修改方法只接受字符串(不允许有变量)。

这里是一个丑陋的黑客瓦特/包含功能:

declare @table table ([column] xml) 
insert @table ([column]) values ('<r><i>1</i><i>2</i><i>3</i></r>') 

declare @parameter xml 
set @parameter = '<r><i>1</i><i>2</i></r>' 

-- build a pipe-delimited string 
declare @in nvarchar(max) 
set @in = convert(nvarchar(max), 
    @parameter.query('for $i in (/r/i) return concat(string($i),"|")') 
) 
set @in = '|'+replace(@in,'| ','|') 

update @table set [column].modify (' 
    delete (//i[contains(sql:variable("@in"),concat("|",.,"|"))]) 
    ') 
select * from @table 

这里的另一个W /动态SQL:

-- replace table variable with temp table to get around variable scoping 
if object_id('tempdb..#table') is not null drop table #table 
create table #table ([column] xml) 
insert #table ([column]) values ('<r><i>1</i><i>2</i><i>3</i></r>') 

declare @parameter xml 
set @parameter = '<r><i>1</i><i>2</i></r>' 

-- we need dymamic SQL because the XML modify method only permits string literals 
declare @sql nvarchar(max) 
set @sql = convert(nvarchar(max), 
    @parameter.query('for $i in (/r/i) return concat(string($i),",")') 
) 
set @sql = substring(@sql,1,len(@sql)-1) 
set @sql = 'update #table set [column].modify(''delete (//i[.=('[email protected]+')])'')' 
print @sql 
exec (@sql) 

select * from #table 

如果要更新XML变量,而不是列,使用sp_executesql的和输出参数:

declare @xml xml 
set @xml = '<r><i>1</i><i>2</i><i>3</i></r>' 

declare @parameter xml 
set @parameter = '<r><i>1</i><i>2</i></r>' 

declare @sql nvarchar(max) 
set @sql = convert(nvarchar(max), 
    @parameter.query('for $i in (/r/i) return concat(string($i),",")') 
) 
set @sql = substring(@sql,1,len(@sql)-1) 
set @sql = 'set @xml.modify(''delete (//i[.=('[email protected]+')])'')' 
exec sp_executesql @sql, N'@xml xml output', @xml output 

select @xml 

使用游标遍历delete val的替代方法UE的,可能不太有效,因为将多个更新程序:对XML变量的限制

declare @table table ([column] xml) 
insert @table ([column]) values ('<r><i>1</i><i>2</i><i>3</i></r>') 

declare @parameter xml 
set @parameter = '<r><i>1</i><i>2</i></r>' 

/* 
-- unfortunately, this doesn't work: 
update t set [column].modify('delete (//i[.=sql:column("p.i")])') 
from @table t, (
    select i.value('.', 'nvarchar') 
    from @parameter.nodes('//i') a (i) 
) p (i) 
select * from @table 
*/ 

-- so we have to use a cursor 
declare @cursor cursor 
set @cursor = cursor for 
    select i.value('.', 'varchar') as i 
    from @parameter.nodes('//i') a (i) 

declare @i int 
open @cursor 
while 1=1 begin 
    fetch next from @cursor into @i 
    if @@fetch_status <> 0 break 
    update @table set [column].modify('delete (//i[.=sql:variable("@i")])') 
end 

select * from @table 

更多信息,SQL2005这里:

http://blogs.msdn.com/denisruc/archive/2006/05/17/600250.aspx

有没有人有更好的办法?