2009-06-19 40 views
5

tôi có:Xóa Nhiều Nodes trong Độc thân & XQuery cho SQL Server

  1. một bảng với một xml loại cột (danh sách các ID)
  2. một xml tham số loại (còn danh sách các ID)

Cách tốt nhất để xóa các nút khỏi cột khớp với các nút trong tham số, trong khi để lại bất kỳ nút chưa khớp nào bị ảnh hưởng?

ví dụ:

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*)') 

Các tài liệu MSDN chỉ ra chúng ta có thể (trong Introduction to XQuery in SQL Server 2005):

thủ tục lưu trữ này có thể dễ dàng được sửa đổi để chấp nhận một đoạn XML mà chứa một hoặc nhiều kỹ năng yếu tố do đó cho phép người dùng để xóa nhiều nút kỹ năng với một yêu cầu duy nhất về thủ tục được lưu trữ .

Trả lời

3

Trong khi xóa là một chút khó xử để thực hiện theo cách này, bạn có thể thực hiện cập nhật để thay đổi dữ liệu, miễn là dữ liệu của bạn đơn giản (chẳng hạn như ví dụ bạn đã cung cấp). Truy vấn sau về cơ bản sẽ chia hai chuỗi XML thành các bảng, nối chúng, loại trừ các giá trị không null (kết hợp) và chuyển đổi nó thành 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

Bạn cần một cái gì đó theo hình thức:

[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 không hỗ trợ loại xml SQL trong SQL2005, và sửa đổi phương pháp duy nhất chấp nhận xâu (không biến cho phép).

Dưới đây là một hack xấu xí w/chứa hàm:

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 

Đây là một w/SQL động:

-- 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 

nếu bạn đang cập nhật biến xml chứ không phải là một cột, sử dụng sp_executesql và thông số đầu ra:

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 

Phương pháp thay thế bằng cách sử dụng con trỏ để lặp qua xóa val ues, có lẽ kém hiệu quả do nhiều cập nhật:

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 

Thông tin thêm về những hạn chế của các biến XML trong SQL2005 ở đây:

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

Có ai có một cách tốt hơn?

Các vấn đề liên quan