2012-01-19 4 views
2

다음과 같은 문제점이 있으며 누군가 도와 드릴 수 있습니다.SQL 쿼리를 사용하여 XML 편집 SQL Server 2008 R2

몇 천 개의 행이있는 SQL Server 데이터베이스가 있습니다. 모든 행은 ID가있는 열과 XML 데이터가있는 열로 구성됩니다. 모든 고유 OCC를 들어

  1. (선두로부터), 태그 조합 : 나는 다음과 같은 규칙에 따라 모든 레코드에 대해이 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 개의 @variant 속성 만 true 일 수 있습니다.

  2. a가 @ lang = en-US 속성을 갖는 경우 @invariant는 'true'여야합니다. 동일한 경우, 태그 조합이있는 나머지 필드는 '거짓'으로 남아 있어야합니다. (샘플 코드에서 태그 aa와 유사)
  3. a가 @lang = nl-NL 속성을 가지지 만 @lang = en-US가없는 경우 @invariant는 'nl-NL'의 'true'여야합니다. 동일한 경우, 태그 조합이있는 나머지 필드는 '거짓'으로 남아 있어야합니다. (샘플 코드에서 태그 ac와 유사)
  4. 태그 조합의 인스턴스가 하나 뿐인 경우 @invariant는 'true'여야합니다. 그래서 @lang 값과 독립적입니다. 내 문제는 올바른 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 쿼리를 실행 한 후

(샘플 코드에서 태그 AE 등), 코드가 보일 것입니다 위의 규칙에 따라 기록한다.

지금까지 나는이 함께했다 : '사실'에 @invariant의 모든 값을 편집

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"') 

합니다.

누군가 올바른 쿼리를 작성하도록 도와 줄 수 있습니까? 미리 감사드립니다!

+1

이와 같은 경우 LINQ를 사용하는 것이 훨씬 쉽습니다. –

답변

1

XML을 파쇄하고 en-US을 먼저 주문하고 nl-NL을 주문하는 order by 절과 함께 row_number()을 사용합니다.
두 번째 row_number()을 사용하여 각 행 (ID 및 행 번호)에 대해 고유 한 키를 생성하십시오.
값을 테이블 변수에 저장하십시오.
최대 행 번호를 가져 와서 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 - 멋진 :). 맥주가 내 방식으로 올 때까지 당신은 대답을 받아 들일 수 있다고 생각할 수 있습니다. 여기에 대한 자세한 내용은 http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work –

+1

을 참조하십시오. Sry,이 커뮤니티에 새로운. 어쨌든 : 다시 한번 감사드립니다. – Jannibal