Редактировать XML с помощью SQL-запроса SQL Server 2008 R2 - PullRequest
2 голосов
/ 19 января 2012

У меня есть следующая проблема, и я надеюсь, что кто-то может помочь.

У меня есть база данных SQL Server с несколькими тысячами строк. Каждая строка состоит из столбца с идентификатором и столбца с данными 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. Для каждого уникального события (вхождения), только для комбинации тегов 1 @invariant атрибут может быть истинным
  2. Если a имеет атрибут @ lang = en-US, то @invariant должен быть равен 'true'. Остальные поля с одинаковой окк, комбинация тегов должны оставаться «ложными». (например, тег aa в примере кода)
  3. Если a имеет атрибут @ lang = nl-NL, но не @ lang = en-US, тогда @invariant должен быть равен 'true' для 'nl-NL'. Остальные поля с одинаковой окк, комбинация тегов должны оставаться «ложными». (например, тег ac в примере кода)
  4. Если у комбинации occ, tag есть только 1 экземпляр, тогда @invariant должен быть 'true'. Так что не зависит от значения @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'.

Может ли кто-нибудь помочь мне построить правильный запрос? Заранее спасибо!

1 Ответ

1 голос
/ 20 января 2012

Измельчите ваш XML и используйте row_number() с предложением order by, которое упорядочивает en-US первый и nl-NL второй.
Используйте секунду row_number() для генерации уникального ключа для каждой строки (ID и RowNumber).
Сохранить значения в табличной переменной.
Получить максимальный номер строки и обновить цикл ia XML для каждого номера строки.

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

Рабочий образец можно найти здесь .

...