[英]iterate through XML sql insert
I have three tables table1, table2, table3
with col1, col2
and identity ID
column. 我有三个表
table1, table2, table3
带有col1, col2
和标识ID
列的col1, col2
。 These table relationship are defined in the database. 这些表关系在数据库中定义。
I am trying to create a stored procedure that accepts xml string input and save that data into tables. 我正在尝试创建一个存储过程,该过程接受xml字符串输入并将该数据保存到表中。
This is the XML input 这是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>
This xml is coming from a third party object and we don't have control to modify the third party object to emit a different format xml. 此xml来自第三方对象,我们无权修改第三方对象以发出不同格式的xml。
Algorithm: 算法:
Is this the only way to handle this situation? 这是处理这种情况的唯一方法吗? If so how to iterate through xml node?
如果是这样,如何遍历xml节点?
Please help!! 请帮忙!!
Thanks, 谢谢,
Esen 也先
Using merge and output
you can do this without a loop using a technique described here . 使用合并和
output
您可以使用此处介绍的技术无循环地完成此操作 。
I assume your table structure is like this. 我假设您的表结构是这样的。
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)
)
The code 编码
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 (select "Text-only results" to see all resultsets) SE-Data (选择“纯文本结果”以查看所有结果集)
If your code sample is representative of the kind of data you get, and strictly adheres to a consistent structure, you could try making a class to deserialize. 如果您的代码示例可以代表您获取的数据类型,并且严格遵循一致的结构,则可以尝试创建一个反序列化的类。 Here is an example set of classes that will correctly deserialize from your given XML sample:
这是一组示例示例,将从正确的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; }
}
Example code that will apply the deserialization: 将应用反序列化的示例代码:
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);
From here, it's as simple as looping through the contents of MyCustomStructure
with for each loops and applying your custom database insert logic: 从这里开始,就像在每个循环中遍历
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
}
}
}
If you're worried about performance with the scale of data you are inserting, you can try passing the data as table-valued parameters, or some other format that you can easily parse on the SQL side. 如果您担心要插入的数据量的性能,可以尝试将数据作为表值参数或其他可以在SQL端轻松解析的格式传递。 You can also bulk upload all the table1 entries, get all the ID's back, then bulk upload all the table2 entries with the correct mapped ID's, get all the new ID's back, etc., which would require a total of 3 or so round trips and should be pretty fast.
您还可以批量上载所有table1条目,获取所有ID的后缀,然后批量上载所有具有正确映射ID的table2条目,获取所有新ID的后缀,依此类推,这总共需要3次往返并且应该很快。
Thought it would be helpful to someone looking for similar solution. 认为这对寻求类似解决方案的人会有所帮助。 This is how I handled traversing nodes in SQL
这就是我在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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.