2

私は次の問題を抱えており、誰かが助けてくれることを願っています。

数千行のSQLServerデータベースがあります。すべての行は、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属性のみがtrueになるタグの組み合わせ
  2. aに@lang= en-US属性がある場合、@invariantは「true」である必要があります。同じocc、タグの組み合わせを持つ残りのフィールドは「false」のままである必要があります。(サンプルコードのタグaaのように)
  3. aに@lang= nl-NL属性があり、@ lang = en-USがない場合、@invariantは「nl-NL」に対して「true」である必要があります。同じocc、タグの組み合わせを持つ残りのフィールドは「false」のままである必要があります。(サンプルコードのタグacのように)
  4. occ、タグの組み合わせにインスタンスが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」に編集します。

誰かが正しいクエリを作成するのを手伝ってもらえますか?前もって感謝します!

4

1 に答える 1

1

XMLを細断処理し、 1番目と2番目に順序付けするorderbyrow_number()句で使用します。 秒を使用して、各行(IDおよびRowNumber)の一意のキーを生成します。 値をテーブル変数に格納します。 最大行番号を取得し、各行番号のXMLiaループを更新します。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 に答える