2012-05-22 69 views
2

我有三個表table1, table2, table3col1, col2和身份ID列。這些表關係在數據庫中定義。迭代通過XML SQL INSERT

我試圖創建一個接受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. 獲取最後一個標識值與去年的標識值
  4. 呼叫子節點作爲外鍵
  5. 做,直到不更多的子節點

是個處理這種情況的唯一方法是什麼?如果是的話如何通過XML節點迭代?

請幫忙!!

感謝,

也先

+0

要回答通過XML節點進行迭代,你可以使用[**'XmlTextReader' **](http://msdn.microsoft.com/en-us/library/system.xml.xmltextreader.aspx)從文件/流中讀取,[**'XmlNodeReader' **](http:// msdn.microsoft.com/en-us/library/system.xml.xmlnodereader.aspx)從「XmlElement」實例讀取。我建議通過嘗試在SQL Server中處理XML來做到這一點。 – mellamokb

+0

你使用什麼版本的SQL Server? –

+0

Sql server 2008.我剛發佈了我自己的答案。如果您有不同的處理方法,請發佈您的解決方案。感謝 – Esen

回答

1

使用mergeoutput你可以做到這一點,而不使用的技術循環描述here

我假設你的表結構是這樣的。

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-Data(選擇 「僅文本結果」 可以看到所有結果集)

+0

這正是我所需要的,非常感謝你。 – Esen

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爲每個循環和應用自定義數據庫插入邏輯:

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次左右的往返並且應該很快。

+0

感謝您的代碼。我沒有問題通過C#處理這個問題。我正在尋找可以在SQL存儲過程中處理此問題的解決方案。我試圖解決在sql server和我的應用程序之間來回切換的次數。 – Esen

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 @[email protected]+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;