2012-01-19 12 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属性のみが真である可能性があります

  2. aが@ lang = en-US属性を持つ場合、@invariantは 'true'でなければなりません。同じocc、tagの組み合わせを持つ残りのフィールドは、「false」のままでなければなりません。 (サンプルコードではタグaaのように)
  3. aが@ lang = nl-NL属性を持ち、@ lang = en-USを持たない場合、@invariantは 'nl-NL'の 'true'でなければなりません。同じocc、tagの組み合わせを持つ残りのフィールドは、「false」のままでなければなりません。 (サンプルコードのタグacのような)
  4. タグの組み合わせのインスタンスが1つしかない場合、@invariantは 'true'にする必要があります。したがって、@lang値とは無関係です。 (サンプルコードでのAEタグなど)

1つの以上のSQLクエリを実行した後、コードは次のようになります。私の問題はすべてのために、すべてのノードを置き換えるために、正しい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> 

上記の規則に従って、記録する。

は、これまでのところ、私はこの思い付いた:「真」に@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を破棄し、row_number()を使用して、en-USを最初に注文し、nl-NLを注文するorder by節を使用します。
2番目の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

関連する問題