【问题标题】:Process XML in stored procedure - Exception generation - log error在存储过程中处理 XML - 异常生成 - 日志错误
【发布时间】:2018-09-05 05:23:41
【问题描述】:

我需要从 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>

我需要在 Table1 中插入父级数据(在父级标签 - Title、Code、Name、company 下),在 Table2 中插入子级数据。 Table2 对 Table1 有 foriengy 键引用。(一旦插入父记录,我们需要使用范围标识来获取它。)

这可能是非常大的 xml 文件。主要问题是我需要一个接一个地插入记录,这意味着在循环中,好像第一个 Parent 有一些问题,那么这个过程不应该被中止,但应该记录错误并继续到第二个或下一个 Parent 标签。

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

TSQL Inserting records from XML string

How to get individual identity in an XML Insert?

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

请提出建议。

【问题讨论】:

  • XML 支持高度特定于供应商 - 所以请添加标签以指定您是否使用mysqlpostgresqlsql-serveroracledb2 - 或完全不同的东西。
  • 我真的希望您的 xml 不使用 &lt;Parent1&gt;&lt;Parent2&gt; 而是使用 &lt;Parent id="1"&gt;&lt;Parent id="2"&gt;... 与孩子一样... 您已将其标记为 @ 987654333@。这正在缩小选择范围,但仍不清楚...
  • FK 是否通过给定的&lt;Code&gt; 构建(在这种情况下必须是唯一的)?
  • 对不起,我错过了您的 cmets...不是 等,只是 。与子元素相同...我写错了...更新了问题。谢谢。

标签: c# sql .net xml tsql


【解决方案1】:

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

如果这是 SQL-Server(我采用 [tsql] 标记的形式),您可以创建一个 STORED PROCEDURE 接受 XML 作为参数并使用以下代码将其分解为临时表。

提示:我使用* 读取您的节点&lt;Parent1&gt;&lt;Parent2&gt;。我希望你没有 name-numbered 元素。但无论如何它都会起作用......

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),所有父行都编号,所有子行都在内部编号。

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

【讨论】:

  • 太棒了!!!感谢您的回答。抱歉回复晚了。是的,我只移动到存储过程并尝试在那里处理 xml。我尝试创建两个临时表,一个带有父级数据,另一个带有父+子数据(平面记录)。我能够插入行,但在某些情况下它给了我唯一的约束错误(作为我的要求的一部分)并且在那种情况下失败了。我一直在寻找提供身份(给父母和孩子的 ID 列)来解决这些问题,这是你的答案,给了我我需要的东西!我会试试你的解决方案。非常感谢!
  • 效果很好!!再次感谢 !!!发布问题时没想到这么干净的答案:)
【解决方案2】:

我将结果放入数据表中。您可以使用对数据库的写入替换对数据表的写入。 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);
                }
            }


        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-18
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多