2

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

我有一个包含几千行的 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 必须为“真”。具有相同 occ、标签组合的其余字段必须保持“假”。(如示例代码中的标记 aa)
  3. 如果 a 具有 @lang=nl-NL 属性,但没有 @lang=en-US,则对于 'nl-NL',@invariant 必须为 'true'。具有相同 occ、标签组合的其余字段必须保持“假”。(如示例代码中的标记 ac)
  4. 如果一个 occ、标签组合只有 1 个实例,则 @invariant 必须为“真”。所以独立于@lang 值。(如示例代码中的标签 ae)

运行 1 个或多个 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”。

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

4

1 回答 1

1

切碎您的 XML 并与排序第一和第二row_number()的 order by 子句一起使用。 使用一秒钟为每一行(ID 和 RowNumber)生成一个唯一键。 将值存储在表变量中。 获取最大行号并更新每个行号的 XML ia 循环。en-USnl-NL
row_number()

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

可以在此处找到工作示例。

于 2012-01-20T07:15:09.627 回答