存储过程中的处理XML - 异常生成 - 日志错误

时间:2018-03-26 23:16:44

标签: c# sql .net xml tsql

我需要从XML文件中将行插入数据库表。我的XML格式如下:

<Main>
    <Parent>
        <Title>Title1</Title>
        <Code>ABC123</Code>
        <Name>name1</Name>
        <company>test1</company>
        <Children>
            <Child>
                <Title>t1</Title>               
                <ContentType>T1</ContentType>
                <TimeStarted>2018-03-01T10:47:46</TimeStarted>
                <TimeFinished>2018-03-01T10:48:08</TimeFinished>
            </Child>
            <Child>
                <Title>t2</Title>               
                <ContentType>T1</ContentType>
                <TimeStarted>2018-03-01T10:47:46</TimeStarted>
                <TimeFinished>2018-03-01T10:48:08</TimeFinished>    
            </Child>
        </Children>
    </Parent>
    <Parent>
        <Title>Title2</Title>
        <Code>def123</Code>
        <Name>name2</Name>
        <company>test2</company>
        <Children>
            <Child>
                <Title>t1</Title>               
                <ContentType>T1</ContentType>
                <TimeStarted>2018-03-01T10:47:46</TimeStarted>
                <TimeFinished>2018-03-01T10:48:08</TimeFinished>
            </Child>
            <Child>
                <Title>t2</Title>               
                <ContentType>T1</ContentType>
                <TimeStarted>2018-03-01T10:47:46</TimeStarted>
                <TimeFinished>2018-03-01T10:48:08</TimeFinished>
            </Child>
        </Children>
    </Parent>
</Main>

我需要在表1中插入父级别数据(在父标记下 - 标题,代码,名称,公司),在表2中插入子级别数据。 表2具有对表1的foriengy键引用(我们需要在插入父记录后使用范围标识获取。)

这可能是非常大的xml文件。主要问题是我需要逐个插入记录,意味着在循环中,好像第一个父有一些问题,然后不应该中止该过程,但应该记录错误并继续进行第二个或下一个父标记。

我尝试搜索最佳方法,但大多数建议都不是通过xml迭代并直接插入表。以下是我尝试过的一些参考资料。

TSQL Inserting records from XML string

How to get individual identity in an XML Insert?

我需要建议,特别是我的要求可以在没有循环的情况下实现? 这也是最好的方法,比如先插入一些临时表然后处理它或者使用xquery首先获取平面数据中的所有记录,然后将光标放在它上面?或任何其他方法

请建议。

2 个答案:

答案 0 :(得分:1)

一般来说,使用两步导入是个好主意,特别是在您预期会出现问题的情况下。

如果这是SQL-Server(我将此表单作为[tsql]标记),您可以创建一个STORED PROCEDURE接受XML作为参数,并使用以下代码将其粉碎成临时表。 / p>

提示:我使用*来阅读您的节点<Parent1><Parent2>。我希望你没有名字编号的元素。但它无论如何都会起作用......

DECLARE @xml XML=N'Your xml here';

WITH Parents AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ParentID
          ,prnt.value(N'(Title)[1]','nvarchar(max)') AS Title
          ,prnt.value(N'(Code)[1]','nvarchar(max)') AS Code
          ,prnt.value(N'(Name)[1]','nvarchar(max)') AS Name
          ,prnt.value(N'(company)[1]','nvarchar(max)') AS Company
          ,prnt.query(N'Children/*') AS ChildrenXML
    FROM @xml.nodes(N'/Main/*') AS Lvl1(prnt)
)
SELECT   Parents.ParentID
        ,Parents.Title
        ,Parents.Code
        ,Parents.Name
        ,Parents.Company
        ,ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY (SELECT NULL)) AS ChildID
        ,chld.value(N'(Title)[1]','nvarchar(max)') AS Child_Title
        ,chld.value(N'(ContentType)[1]','nvarchar(max)') AS Child_ContentType
        ,chld.value(N'(TimeStarted)[1]','nvarchar(max)') AS Child_TimeStarted
        ,chld.value(N'(TimeFinished)[1]','nvarchar(max)') AS Child_TimeFinished
INTO #StagingTable
FROM Parents
OUTER APPLY ChildrenXML.nodes(N'*') AS Lvl2(chld);

SELECT * FROM #StagingTable;

结果

+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+
| ParentID | Title  | Code   | Name  | Company | ChildID | Child_Title | Child_ContentType | Child_TimeStarted   | Child_TimeFinished  |
+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+
| 1        | Title1 | ABC123 | name1 | test1   | 1       | t1          | T1                | 2018-03-01T10:47:46 | 2018-03-01T10:48:08 |
+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+
| 1        | Title1 | ABC123 | name1 | test1   | 2       | t2          | T1                | 2018-03-01T10:47:46 | 2018-03-01T10:48:08 |
+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+
| 2        | Title2 | def123 | name2 | test2   | 1       | t1          | T1                | 2018-03-01T10:47:46 | 2018-03-01T10:48:08 |
+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+
| 2        | Title2 | def123 | name2 | test2   | 2       | t2          | T1                | 2018-03-01T10:47:46 | 2018-03-01T10:48:08 |
+----------+--------+--------+-------+---------+---------+-------------+-------------------+---------------------+---------------------+

这是非常宽容的。所有目标列都是NVARCHAR(MAX),所有父行都有编号,所有子行都在内部编号。

第二步 - 将其转移到目标表 - 可以从这里轻松完成。您可以包括任何类型的评估,日志记录和/或错误处理。

答案 1 :(得分:0)

我将结果放入数据表中。您可以使用对数据库的写入来替换对数据表的写入。 Ir从代码中读取数据,然后写入数据库。它取决于您想要使用的方法的数据库大小。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Data;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Parent", typeof(string));
            dt.Columns.Add("Parent Title", typeof(string));
            dt.Columns.Add("Code", typeof(string));
            dt.Columns.Add("Name", typeof(string));
            dt.Columns.Add("Company", typeof(string));

            dt.Columns.Add("Child", typeof(string));
            dt.Columns.Add("Child Title", typeof(string));
            dt.Columns.Add("Content Type", typeof(string));
            dt.Columns.Add("Time Start", typeof(DateTime));
            dt.Columns.Add("Time Finish", typeof(DateTime));

            XDocument doc = XDocument.Load(FILENAME);

            List<XElement> parents = doc.Root.Elements().ToList();

            foreach (XElement parent in parents)
            {
                try
                {
                    string parentTagName = (string)parent.Name.LocalName;
                    string parentTitle = (string)parent.Element("Title");
                    string code = (string)parent.Element("Code");
                    string parentName = (string)parent.Element("Name");
                    string company = (string)parent.Element("company");
                    foreach (XElement child in parent.Element("Children").Elements())
                    {
                        try
                        {
                            string childTagName = child.Name.LocalName;
                            string childTitle = (string)child.Element("Title");
                            string contentType = (string)child.Element("ContentType");
                            DateTime timeStarted = (DateTime)child.Element("TimeStarted");
                            DateTime timeFinished = (DateTime)child.Element("TimeFinished");

                            dt.Rows.Add(new object[] {
                                parentTagName,
                                parentTitle,
                                code,
                                parentName,
                                company,
                                childTagName,
                                childTitle, 
                                contentType,
                                timeStarted,
                                timeFinished

                            });
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }

                    }
                }

                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }


        }
    }
}