【问题标题】:Have XML file need it to populatemultiple SQL Tables有 XML 文件需要它来填充多个 SQL 表
【发布时间】:2011-03-24 19:57:36
【问题描述】:

我有一个 XML 文件,我需要填充多个 SQL 表,我想知道最好的方法是什么。我在考虑数据集或 xslt,但老实说我不确定。这是我生成的 XML(部分)

 <?xml version="1.0" encoding="utf-8" standalone="yes" ?> 
- <!-- Created: 8/3/2010 12:09:15 PM
  --> 
- <Trip>
- <TripDetails>
  <DepartureDate /> 
  <ReturnDate /> 
  <TripTypeA>3</TripTypeA> 
  <TripTypeB>1</TripTypeB> 
  <PurposeOfTrip>vacation</PurposeOfTrip> 
  <Region>5</Region> 
- <Countries>
  <Country>105</Country> 
  <Country>135</Country> 
  </Countries>
- <Cities>
  <City>Cancun</City> 
  <City>Tokyo</City> 
  <City>Mayo</City> 
  </Cities>
  <OverallRating>4</OverallRating> 
  <Suppliers>53</Suppliers> 
  <SuppliersComments>Good flight</SuppliersComments> 
- <Transport>
  <TransportType>1</TransportType> 
  <TransportType>3</TransportType> 
  </Transport>
  <TransportComment>Transportation was fast</TransportComment> 

我需要填充几个不同的表。(例如保持简短)

TripDetails (TripID, TripTypeA, TripTypeB, SupplierID, overallRating)
TripCountries (TripCountryID, TripID, CountryCode)

我还有很多表(城市、交通),但如果我能弄清楚如何更新 TripDetails(主表)和 TripCountries(将 TripDetails 和国家/地区结合在一起的表),我想我会很好,谢谢!

【问题讨论】:

  • 我认为应该用&lt;/TripDetails&gt;&lt;/Trip&gt; 关闭它吗??
  • 对对,对不起,我刚刚复制了我的 xml 的 sn-p
  • 使用“不可知”XSLT,您只能将此 XML 转换为 SQL 更新查询。如果这不是你想要的,请重新标记并添加你的 SQL 引擎,因为也许有一个供应商特定的方法来完成工作,比如 mattmc3 的答案。

标签: sql-server linq ado.net dataset


【解决方案1】:

假设您使用的是 SQL Server,您应该将 XML 解析为 DataTables 并使用 SqlBulkCopy 对象以超快的速度将它们发送到数据库中。有很多资源可以帮助您了解 SqlBulkCopy。这是另一个 StackOverflow 问题的最新讨论,可帮助您入门:Sql Server 2008 Tuning with large transactions (700k+ rows/transaction)

如果 XML 文件真的很大,你应该小心你使用什么样的解析器。 XDocument 和 XmlDocument 将整个内容加载到内存中。如果文件足够小,比如小于 10MB,那么使用这些解析器应该没问题。


编辑:

这里有一个关于如何将 XML 导入 DataTables 的快速模型。它在 VB 中,因为 VB 使 XML 变得更容易。

Option Strict On : Option Explicit On : Option Infer On : Option Compare Binary

Imports System.Data
Imports System.Linq
Imports System.Xml.Linq

Module Module1

    Sub Main()
      Dim xml =
         <Trip>
            <TripDetails id="1">
               <DepartureDate/> 
               <ReturnDate/> 
               <TripTypeA>3</TripTypeA> 
               <TripTypeB>1</TripTypeB> 
               <PurposeOfTrip>vacation</PurposeOfTrip> 
               <Region>5</Region> 
               <Countries>
                  <Country>105</Country> 
                  <Country>135</Country> 
               </Countries>
               <Cities>
                  <City>Cancun</City> 
                  <City>Tokyo</City> 
                  <City>Mayo</City> 
               </Cities>
               <OverallRating>4</OverallRating> 
               <Suppliers>53</Suppliers> 
               <SuppliersComments>Good flight</SuppliersComments> 
               <Transport>
                  <TransportType>1</TransportType> 
                  <TransportType>3</TransportType> 
               </Transport>
               <TransportComment>Transportation was fast</TransportComment>
            </TripDetails>
         </Trip>

         Dim dtTripDetails As New DataTable()
         With dtTripDetails.Columns
            .Add("TripID", GetType(Integer))
            .Add("TripTypeA", GetType(Integer))
            .Add("DepartureDate", GetType(DateTime))
            .Add("TransportComment", GetType(String))
         End With

         Dim dtTripDetailXrefCountries As New DataTable()
         With dtTripDetailXrefCountries.Columns
            .Add("TripID", GetType(Integer))
            .Add("CountryID", GetType(Integer))
         End With

         Dim xdetails = From td In xml.Descendants("TripDetails") Select td
         For Each xdetailRecord As XElement In xdetails
            Dim tripID As Integer = CInt(xdetailRecord.Attribute("id").Value)
            Dim tripTypeA As Integer = CInt(xdetailRecord.Element("TripTypeA").Value)
            Dim strDepDate As String = xdetailRecord.Element("DepartureDate").Value
            Dim depDate As Object = If(String.IsNullOrEmpty(strDepDate), CType(DBNull.Value, Object), CType(DateTime.Parse(strDepDate), Object))
            Dim transportComment As String = xdetailRecord.Element("TransportComment").Value
            dtTripDetails.Rows.Add(tripID, tripTypeA, depDate, transportComment)

            Dim xcountries = From c In xdetailRecord.Element("Countries").Elements("Country") Select c
            For Each xcountryRecord As XElement In xcountries
               Dim countryID As Integer = CInt(xcountryRecord.Value)
               dtTripDetailXrefCountries.Rows.Add(tripID, countryID)
            Next
         Next

         Console.WriteLine("Done")
         Console.ReadKey(True)

    End Sub

End Module

顺便说一句 - 在执行此类 ETL 时,最好先将数据泵入暂存表,而不是直接输入生产表。这样一来,您就可以验证数据类型并确保引用完整性并处理密钥管理,并在不锁定或污染您的生产表的情况下使所有内容处于完美位置。

【讨论】:

  • 感谢您的快速回复,我会调查的。我正在创建的文件小于 2mb(否则我应该使用什么?只是好奇)并且我使用的是 SQL Server 2008。所以对于我的 SQL Server 中的每个表,我应该创建一个新的 DataTable,然后使用 SqlBulkCopy 插入?
  • 对于如何仅将某些部分添加到数据表/数据集仍然有点困惑
  • 1 个 DataTable 映射到 1 个 SQL 表,所以是的 - 您需要一个 1 对 1 的映射。您提到对不同部分感到困惑-我假设您在询问如何获取 XML 文件中的不同数据。如果您使用旧的 XmlDocument,您可能需要 XPath。但是,如果您可以使用 3.5 框架,我建议您使用 XDocument,然后您可以使用 Linq 查询您的 XML 文件并获取您需要的数据元素。因此,您将从循环遍历 TripDetails 节点并填充 TripDetails 数据表开始,然后选择子节点并填充这些表。
  • 非常感谢,我会尽力把它翻译成C#,谢谢一百万
【解决方案2】:

您可以很容易地使用 SQL Server 2005 的 XQuery 功能,例如有一个接受这些 XML 字符串作为输入的存储过程:

CREATE PROCEDURE dbo.StoreTripDetails(@XmlContent XML)
AS BEGIN
   DECLARE @NewTripID INT

   INSERT INTO dbo.TripDetails(TripTypeA, TripTypeB, SupplierID, overallRating)
       SELECT
        Trip.Details.value('(TripTypeA)[1]', 'int') 'TripTypeA',
        Trip.Details.value('(TripTypeB)[1]', 'int') 'TripTypeB',
        Trip.Details.value('(Suppliers)[1]', 'int') 'SupplierID',
        Trip.Details.value('(OverallRating)[1]', 'int') 'OverallRating'
       FROM
        @XmlContent.nodes('/Trip/TripDetails') AS Trip(Details)

    SELECT @NewTripID = SCOPE_IDENTITY()

    INSERT INTO dbo.TripCountries(TripID, CountryCode)
        SELECT
        @NewTripID, Trip.Countries.value('(.)[1]', 'int') 
        FROM
        @XmlContent.nodes('/Trip/TripDetails/Countries/Country') AS Trip(Countries)
 END

然后你可以很容易地从你的 C# 代码中调用这个存储过程,并传入一个代表一次旅行的 XML 块。

【讨论】:

    【解决方案3】:

    您需要指定您使用的数据库引擎。由于您已经指定了 Linq 标记,我假设您使用的是 Microsoft SQL 服务器。

    您可以轻松地将文档作为字符串传递给存储过程。在此存储过程中,您定义了一些临时表模式并使用 sp_xml_preparedocument、OPENXML(和相关函数)使用 XML 文档中的数据填充这些临时表。现在,您可以使用标准 SQL 使用您需要的任何投影、连接或其他转换从临时表中插入永久表。大多数时候内存也不再是一个问题,因为它都是在 MSSQL Server 中完成的。

    更多信息: http://msdn.microsoft.com/en-us/library/aa260385(SQL.80).aspx http://msdn.microsoft.com/en-us/library/aa276847(SQL.80).aspx

    示例(未经测试):

    declare @docHandle int
    declare @xmlContent varchar(500)
    set @doc ='<root><items><item value="1"/><item value="2"/></item></root>'
    
    exec sp_xml_preparedocument @docHandle OUTPUT, @xmlContent
    
    insert into myTable ([ItemValue])
    select value from OPENXML(@docHandle, '/root/items/item')
    
    exec sp_xml_removedocument @docHandle 
    

    请注意,这些是 SQL 2000 约定。在 SQL 2005 中,有等效的 T-SQL 约定,它们比这些存储过程的前身更具可读性。但是,我相信这些约定在 SQL 2005 和 SQL 2008 中仍然可用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      相关资源
      最近更新 更多