使用SQL查询SQL Server 2008 R2编辑XML

时间:2012-01-19 17:07:49

标签: sql-server xml sql-server-2008 sqlxml

我有以下问题,希望有人可以提供帮助。

我有一个有几千行的SQL Server数据库。每一行都包含一个带有ID的列和一个带有XML数据的列。

此XML数据类似于:

<record id="1">
 <field tag="aa" occ="1" lang="nl-NL" invariant="false">Jan</field>
 <field tag="aa" occ="1" lang="en-US" invariant="false">John</field>
 <field tag="aa" occ="1" lang="de-DE" invariant="false">der Jan</field>
 <field tag="aa" occ="2" lang="nl-NL" invariant="false">Jan2</field>
 <field tag="aa" occ="2" lang="en-US" invariant="false">John2</field>
 <field tag="ab" occ="1">Something</field>
 <field tag="ac" occ="1" lang="de-DE" invariant="false">Rechnung</field>
 <field tag="ac" occ="1" lang="nl-NL" invariant="false">rekening</field>
 <field tag="ad" occ="1">Something2</field>
 <field tag="ae" occ="1" lang="nl-NL" invariant="false">stoeptegel</field>
</record>

我想根据以下规则为每条记录编辑此XML:

  1. 对于每个唯一的occ(出现),标签组合只有1个@invariant属性可以为真
  2. 如果a具有@lang = en-US属性,则@invariant必须为'true'。具有相同occ,标记组合的剩余字段必须保持为“false”。 (如示例代码中的标记aa)
  3. 如果a具有@lang = nl-NL属性,但没有@lang = en-US,那么'nl-NL'的@invariant必须为'true'。具有相同occ,标记组合的剩余字段必须保持为“false”。 (如示例代码中的标记ac)
  4. 如果occ,标签组合只有1个实例,则@invariant必须为“true”。所以独立于@lang值。 (如示例代码中的标记ae)
  5. 运行一个或多个SQL查询后,代码应如下所示:

    <record id="1">
     <field tag="aa" occ="1" lang="nl-NL" invariant="false">Jan</field>
     <field tag="aa" occ="1" lang="en-US" invariant="true">John</field>
     <field tag="aa" occ="1" lang="de-DE" invariant="false">der Jan</field>
     <field tag="aa" occ="2" lang="nl-NL" invariant="false">Jan2</field>
     <field tag="aa" occ="2" lang="en-US" invariant="true">John2</field>
     <field tag="ab" occ="1">Something</field>
     <field tag="ac" occ="1" lang="de-DE" invariant="false">Rechnung</field>
     <field tag="ac" occ="1" lang="nl-NL" invariant="true">rekening</field>
     <field tag="ad" occ="1">Something2</field>
     <field tag="ae" occ="1" lang="nl-NL" invariant="true">stoeptegel</field>
    </record>
    

    我的问题是根据上述规则创建正确的SQL查询,以替换所有记录的所有节点。

    到目前为止,我提出了这个问题:

    while exists 
    (
    select * 
    from databasetable 
    where xmlcolumn.exist('/record/field/@invariant[.="false"]') = 1
    )
    
    update databasetable
    set xmlcolumn.modify
    ('replace value of (/record/field/@invariant[.="false"])[1] with "true"')
    

    将@invariant的每个值编辑为'true'。

    有人可以帮我构建正确的查询吗?提前谢谢!

1 个答案:

答案 0 :(得分:1)

粉碎您的XML并使用row_number()一个订购条款,先订购en-US,然后nl-NL秒。 使用第二个row_number()为每行生成唯一键(ID和RowNumber) 将值存储在表变量中 获取最大行号并为每个行号更新XML i循环。

declare @Tmp table
(
  ID int, -- Primary key in databasetable
  RowNumber int,
  Tag varchar(2),
  Occ int,
  Lang varchar(5),
  Invariant bit
  primary key (ID, RowNumber)
);

with C1 as
(
  select T.ID, -- Primary key in databasetable
         R.F.value('@tag', 'varchar(2)') as Tag,
         R.F.value('@occ', 'int') as Occ,
         R.F.value('@lang', 'varchar(5)') as Lang
  from databasetable as T
    cross apply T.xmlcolumn.nodes('/record/field') as R(F)
), 
C2 as
(
  select ID, Tag, Occ, Lang,
         row_number() over(partition by ID order by (select 0)) as RowNumber,
         row_number() over(partition by ID, Tag, Occ 
                           order by case Lang 
                                      when 'en-US' then 1
                                      when 'nl-NL' then 2
                                      else 3
                                    end) as rnInv
  from C1
)
insert into @Tmp (ID, RowNumber, Tag, Occ, Lang, Invariant)
select ID, RowNumber, Tag, Occ, Lang, case rnInv when 1 then 1 else 0 end
from C2;

declare @MaxRowNum int;
declare @I int = 1;

select @MaxRowNum = max(RowNumber)
from @Tmp;

while @I <= @MaxRowNum
begin
  update T
  set xmlcolumn.modify('replace value of (/record/field[@tag = sql:column("Tmp.Tag") and
                                                        @occ = sql:column("Tmp.Occ") and
                                                        @lang = sql:column("Tmp.Lang")]/@invariant)[1] 
                          with sql:column("Tmp.Invariant")')
  from databasetable as T
    inner join @Tmp as Tmp
      on T.ID = Tmp.ID
  where Tmp.RowNumber = @I;

  set @I += 1;
end

可以找到工作样本here