2012-01-19 2 views
2

У меня есть следующая проблема и надеюсь, что кто-то может помочь.Редактировать XML с SQL-запросом SQL Server 2008 R2

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

После запуска 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 в «истину».

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

+1

Было бы проще использовать LINQ в таком случае. –

ответ

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.

+0

Микаэль, ты мой герой на сегодня! Большое спасибо. Я должен тебе пиво. – Jannibal

+0

@ user1158997 - Nice :). Пока пиво не вступит в мой путь, вы можете согласиться с ответом. Подробнее об этом читайте здесь: http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work –

+1

Выполнено. Sry, новый для этого сообщества. Во всяком случае: еще раз спасибо. – Jannibal

Смежные вопросы