2

アイデンティティ列を持つ3つのテーブルtable1, table2, table3があります。これらのテーブルの関係は、データベースで定義されています。col1, col2ID

xml文字列入力を受け入れるストアドプロシージャを作成し、そのデータをテーブルに保存しようとしています。

これはXML入力です

<root>
 <table1 col1='a' col2='b'>
  <table2Array>
   <table2 col1='c' col2='d'>
    <table3array>
     <table3 col1='g' col2='h' />
     <table3 col1='i' col2='j' />
    </table3array>
   </table2>
  <table2 col1='c' col2='d'>
   <table3array>
    <table3 col1='k' col2='l' />
    <table3 col1='i' col2='j' />
   </table3array>
  </table2>
 </table2Array>
</table1>
 <table1 col1='a' col2='b'>
  <table2Array>
   <table2 col1='e' col2='f'>
    <table3array>
     <table3 col1='i' col2='j' />
     <table3 col1='i' col2='j' />
    </table3array>
   </table2>
   <table2 col1='e' col2='f'>
    <table3array>
     <table3 col1='g' col2='h' />
     <table3 col1='g' col2='h' />
    </table3array>
   </table2>
  </table2Array>
 </table1>
</root>

このxmlはサードパーティのオブジェクトからのものであり、サードパーティのオブジェクトを変更して別の形式のxmlを発行するように制御することはできません。

アルゴリズム:

  1. 各ノードをループします
  2. ノード属性をテーブルに挿入します
  3. 最後のID値を取得する
  4. 最後のID値を外部キーとして子ノードを呼び出す
  5. 子ノードがなくなるまで実行します

これがこの状況に対処する唯一の方法ですか?もしそうなら、xmlノードを反復処理する方法は?

助けてください!!

ありがとう、

エセン

4

3 に答える 3

1

マージを使用すると、ここでoutput説明する手法を使用して、ループなしでこれを行うことができます。

テーブルの構造はこんな感じだと思います。

create table Table1
(
  Table1ID int identity primary key,
  Col1 char(1),
  Col2 char(1)
)

create table Table2
(
  Table2ID int identity primary key,
  Table1ID int references Table1(Table1ID),
  Col1 char(1),
  Col2 char(1)
)

create table Table3
(
  Table3ID int identity primary key,
  Table2ID int references Table2(Table2ID),
  Col1 char(1),
  Col2 char(1)
)

コード

declare @T1 table (XMLCol xml, TargetID int);
declare @T2 table (XMLCol xml, TargetID int);

merge Table1 as T
using (select T1.XMLCol.query('*'),
              T1.XMLCol.value('@col1', 'char(1)'),
              T1.XMLCol.value('@col2', 'char(1)')
       from @XML.nodes('/root/table1') as T1(XMLCol)) as S(XMLCol, Col1, Col2)
on 1 = 0
when not matched then
  insert (Col1, Col2) values (S.Col1, S.Col2)
output S.XMLCol, inserted.Table1ID into @T1;          

merge Table2 as T
using (select T2.XMLCol.query('*'),
              T1.TargetID,
              T2.XMLCol.value('@col1', 'char(1)'),
              T2.XMLCol.value('@col2', 'char(1)')
       from @T1 as T1  
         cross apply T1.XMLCol.nodes('table2Array/table2') as T2(XMLCol)) as S(XMLCol, ID1, Col1, Col2)
on 1 = 0
when not matched then
  insert (Table1ID, Col1, Col2) values (S.ID1, S.Col1, S.Col2)
output S.XMLCol, inserted.Table2ID into @T2;          

insert into Table3(Table2ID, Col1, Col2)
select T2.TargetID,
       T3.XMLCol.value('@col1', 'char(1)'),
       T3.XMLCol.value('@col2', 'char(2)') 
from @T2 as T2
  cross apply T2.XMLCol.nodes('table3array/table3') as T3(XMLCol);

SE-データ(すべての結果セットを表示するには、[テキストのみの結果]を選択します)

于 2012-05-24T20:30:36.123 に答える
0

コードサンプルが取得するデータの種類を表しており、一貫した構造に厳密に準拠している場合は、逆シリアル化するクラスを作成してみてください。これは、指定されたXMLサンプルから正しく逆シリアル化するクラスのサンプルセットです。

[XmlRoot("root")]
public class MyCustomStructure
{
    [XmlElement("table1")]
    public Table1Structure[] Table1Array { get; set; }
}

[XmlRoot("table1")]
public class Table1Structure
{
    [XmlAttribute("col1")]
    public string Col1 { get; set; }
    [XmlAttribute("col2")]
    public string Col2 { get; set; }
    [XmlArray("table2Array")]
    [XmlArrayItem("table2")]
    public Table2Structure[] Table2Array { get; set; }
}

[XmlRoot("table2")]
public class Table2Structure
{
    [XmlAttribute("col1")]
    public string Col1 { get; set; }
    [XmlAttribute("col2")]
    public string Col2 { get; set; }
    [XmlArray("table3array")]
    [XmlArrayItem("table3")]
    public Table3Structure[] Table3Array { get; set; }
}

public class Table3Structure
{
    [XmlAttribute("col1")]
    public string Col1 { get; set; }
    [XmlAttribute("col2")]
    public string Col2 { get; set; }
}

デシリアライズを適用するサンプルコード:

var ser = new XmlSerializer(typeof(MyCustomStructure));

// if xml is in a string, use the following:
var sr = new StringReader(xml);
var xr = new XmlTextReader(sr);

// if xml is in a stream, use the following:
var xr = new XmlTextReader(stream);

// if xml is in an XmlElement, use the following:
var xr = new XmlNodeReader(element);

// result contains an instance of MyCustomStructure
var result = ser.Deserialize(xr);

MyCustomStructureここからは、 with for eachループの内容をループし、カスタムデータベース挿入ロジックを適用するのと同じくらい簡単です。

for each (var table1 in result.Table1Array)
{
    // insert table1, get inserted ID
    for each (var table2 in table1.Table2Array)
    {
        // insert table2, use table1 inserted ID, get table2 ID
        for each (var table3 in table2.Table3Array)
        {
            // insert table3, use table2 inserted ID
        }
    }
}

挿入するデータのスケールのパフォーマンスが心配な場合は、データをテーブル値パラメーターとして渡すか、SQL側で簡単に解析できるその他の形式で渡すことができます。また、すべてのtable1エントリを一括アップロードし、すべてのIDを取得してから、正しいマップIDを使用してすべてのtable2エントリを一括アップロードし、すべての新しいIDを取得するなど、合計3回程度の往復が必要になります。かなり速いはずです。

于 2012-05-22T19:50:10.283 に答える
0

同様の解決策を探している人には役立つと思いました。これは私がSQLでトラバースノードを処理した方法です

        declare @xmlRoot as xml 
        set @xmlRoot= '<root>
         <table1 col1="a" col2="b">
          <table2Array>
           <table2 col1="c" col2="d">
            <table3array>
             <table3 col1="g" col2="h" />
             <table3 col1="i" col2="j" />
            </table3array>
           </table2>
          <table2 col1="c" col2="d">
           <table3array>
            <table3 col1="k" col2="l" />
            <table3 col1="i" col2="j" />
           </table3array>
          </table2>
         </table2Array>
        </table1>
         <table1 col1="a" col2="b">
          <table2Array>
           <table2 col1="e" col2="f">
            <table3array>
             <table3 col1="i" col2="j" />
             <table3 col1="i" col2="j" />
            </table3array>
           </table2>
           <table2 col1="e" col2="f">
            <table3array>
             <table3 col1="g" col2="h" />
             <table3 col1="g" col2="h" />
            </table3array>
           </table2>
          </table2Array>
         </table1>
        </root>'
        Declare @col1 varchar(100),@col2 varchar(100), @table1Counter int, @table2Counter int

        select @table1Counter=0

        DECLARE table1_cursor CURSOR FOR 
            SELECT
            col1 =  item.value('./@col1', 'varchar(100)'), 
            col2 =  item.value('./@col2', 'varchar(100)') 
            FROM @xmlRoot.nodes('root/table1') AS T(item);

            OPEN table1_cursor

            FETCH NEXT FROM table1_cursor 
            INTO  @col1 ,@col2

            WHILE @@FETCH_STATUS = 0
                BEGIN
                    --insert into table1 and get id into a variable
                     set @table1Counter=@table1Counter+1


                    DECLARE table2_cursor CURSOR FOR 
                    SELECT
                    col1 =  item.value('./@col1', 'varchar(100)'),
                    col2 = item.value('./@col2', 'varchar(100)') 
                    FROM @xmlRoot.nodes('root/table1[sql:variable("@table1Counter")]/table2Array/table2') AS T(item);                            
                        OPEN table2_cursor
                        FETCH NEXT FROM table2_cursor INTO @col1 ,@col2                       
                        WHILE @@FETCH_STATUS = 0
                            BEGIN
                                --insert into table2 and get id into a varialbe
                                set @table2Counter = @table2Counter+1

                                 --do same for table3 similar to table2

                                FETCH NEXT FROM table2_cursor INTO @col1 ,@col2
                             END
                        CLOSE table2_cursor
                        DEALLOCATE table2_cursor                            
                    FETCH NEXT FROM table1_cursor 
                    INTO  @col1, @col2                      
                END 
        CLOSE table1_cursor;
        DEALLOCATE table1_cursor;
于 2012-05-24T13:41:52.590 に答える