2009-06-19 12 views
5

que tengo:Eliminación de múltiples nodos en Individual XQuery para SQL Server

  1. una tabla con una columna de tipo xml (lista de ID)
  2. un parámetro de tipo xml (también lista de ID)

¿Cuál es la mejor manera de eliminar los nodos de la columna que coinciden con los nodos en el parámetro, dejando intactos los nodos sin coincidencia?

p. Ej.

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

La documentación de MSDN indica que debería ser posible (en Introduction to XQuery in SQL Server 2005):

Este procedimiento almacenado puede ser fácilmente modificado para aceptar un fragmento de XML que contiene uno o más habilidad elementos permitiendo de ese modo el usuario a elimina múltiples nodos de habilidad con una sola invocación del procedimiento almacenado.

Respuesta

3

Si bien la eliminación es un poco incómoda de hacer de esta manera, en su lugar puede hacer una actualización para cambiar los datos, siempre que sus datos sean simples (como el ejemplo que proporcionó). La siguiente consulta básicamente dividirá las dos cadenas XML en tablas, las unirá, excluirá los valores no nulos (coincidentes) y la convertirá de nuevo a 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

necesita algo en la forma:

[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 no admite tipos de SQL XML en SQL2005, y el método de modificar sólo acepta los literales de cadena (sin variables permitidos).

He aquí un truco feo w/la función contiene:

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 

está aquí otra w/SQL dinámico:

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

Si va a actualizar una variable XML en lugar de una columna, sp_executesql uso y parámetros de salida:

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 

Método alternativo usando un cursor para iterar a través de delete val ues, probablemente menos eficiente debido a varias actualizaciones:

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 

Más información sobre las limitaciones de las variables XML en SQL2005 aquí:

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

¿Alguien tiene una mejor manera?

Cuestiones relacionadas