سؤال

أملك:

  1. طاولة مع XML اكتب العمود (قائمة المعرفات)
  2. و 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>'

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

تشير وثائق MSDN إلى أنه يجب أن يكون ممكنًا (في مقدمة إلى Xquery في SQL Server 2005):

يمكن تعديل هذا الإجراء المخزن بسهولة لقبول جزء XML الذي يحتوي على عنصر مهارة واحد أو أكثر مما يتيح للمستخدم حذف عقد مهارة متعددة مع استدعاء واحد للإجراء المخزن.

هل كانت مفيدة؟

المحلول

على الرغم من أن الحذف أمر محرج قليلاً للقيام بهذه الطريقة ، يمكنك بدلاً من ذلك إجراء تحديث لتغيير البيانات ، شريطة أن تكون بياناتك بسيطة (مثل المثال الذي قدمته). سيقوم الاستعلام التالي بتقسيم سلاسل 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
)

نصائح أخرى

أنت بحاجة إلى شيء ما في الشكل:

[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

إليك آخر مع 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[.=('+@sql+')])'')'
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[.=('+@sql+')])'')'
exec sp_executesql @sql, N'@xml xml output', @xml output

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

مزيد من المعلومات حول قيود متغيرات XML في SQL2005 هنا:

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

هل لدى أي شخص طريقة أفضل؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top