2009-06-19 64 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

有沒有人有更好的辦法?