15

假设我有一个这样的表:

CREATE TABLE [dbo].[TBL_XML]
(
   [XmlFileID]       [BIGINT] IDENTITY (1, 1) NOT NULL,
   [FileName]        [NVARCHAR](500) NULL,   
   [XmlData]         [XML] NULL,
   [DateCreated]     [DATETIME] NOT NULL,
)

我目前用来填表的方法是这样的:

using (SqlCommand cmd = new SqlCommand())
{
    cmd.CommandText = @"INSERT INTO [dbo].[TBL_XML] 
                                    ( [XmlData] , 
                                    [FileName] , 
                                    [DateCreated]
                                    ) 
                        VALUES (@XMLData, @FileName, GETDATE())";

    using (var xmlReader = new XmlTextReader(new FileStream(item.XmlFileName, FileMode.Open)))
    {
        cmd.Parameters.Add("@FileName", SqlDbType.NVarChar, 500).Value = System.IO.Path.GetFileName(item.XmlFileName);
        cmd.Parameters.Add(
        new SqlParameter("@XMLData", SqlDbType.Xml)
        {
            Value = new SqlXml(xmlReader)
        });

        SetConnectionParameters(cmd);

        cmd.ExecuteScalar());
    }
}

但这不适用于非常大的 XML,因为整个文件都加载到内存中,并且出现 OutOfMemory 异常。

将大型 (>100MB) XML 文件从运行在与服务器不同的计算机上的 .net 应用程序插入 XMLData 列的最佳方法是什么?

批量插入是不可能的,因为 SQL 服务器将无法访问我的 XML 文件。

4

11 回答 11

1

以下是仅使用 .NET 进行分块的一种潜在方法。我没有尝试执行此操作,但它应该可以工作。

    public static ChunkedXmlInsert(XmlItem item)
    {
        int bufferSize = 65536;

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            CreateTempTable(connection);

            int position = 0;
            using (StreamReader textStream = File.OpenText(item.XmlFileName))
            {
                char[] buffer = new char[bufferSize];
                int length = textStream.Read(buffer, position, buffer.Length);
                long id = InsertFirstBlock(connection, new string(buffer, 0, length));

                while (textStream.EndOfStream == false)
                {
                    position += length;
                    length = textStream.Read(buffer, position, buffer.Length);
                    AppendBlock(connection, id, new string(buffer, 0, length));
                }
            }

            CopyRecordFromTemp(connection, id);
        }
    }

    private static void CreateTempTable(SqlConnection connection)
    {
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.Text;
            command.CommandText = @"CREATE TABLE #TBL_XML (
                                                              [XmlFileID] [BIGINT] IDENTITY (1, 1) NOT NULL PRIMARY KEY,
                                                              [FileName] [NVARCHAR](500) NULL,
                                                              [XmlData] [NVARCHAR(MAX)] NULL,
                                                              [DateCreated] [DATETIME] NOT NULL
                                                          )";
            command.ExecuteNonQuery();
        }
    }

    private static long InsertFirstBlock(SqlConnection connection, string text)
    {
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.Text;
            command.CommandText = @"INSERT INTO #TBL_XML
                                                        ( [XmlData] , 
                                                          [FileName] , 
                                                          [DateCreated]
                                                        ) 
                                        VALUES (@XMLData, @FileName, GETDATE()); SELECT SCOPE_IDENTITY()";

            command.Parameters.AddWithValue("@FileName", System.IO.Path.GetFileName(item.XmlFileName));
            command.Parameters.AddWithValue("@XmlData", text);
            return (long)command.ExecuteScalar();
        }
    }

    private static void AppendBlock(SqlConnection connection, long id, string text)
    {
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.Text;
            command.CommandText = @"UPDATE #TBL_XML
                                            SET XmlData = XmlData + @xmlData
                                    WHERE XmlFileID = @XmlFileID";

            command.Parameters.AddWithValue("@XmlData", text);
            command.Parameters.AddWithValue("@XmlFileID", id);
            command.ExecuteNonQuery();
        }
    }

    private static long CopyRecordFromTemp(SqlConnection connection, long id)
    {
        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.Text;
            command.CommandText = @"INSERT INTO [dbo].[TBL_XML] ([XmlData], [FileName], [DateCreated])
                                    SELECT CONVERT(xml, [XmlData]), [FileName], [DateCreated]
                                    FROM #TBL_XML
                                    WHERE XmlFileID = @XmlFileID; SELECT SCOPE_IDENTITY()";
            return (long)command.ExecuteScalar();
        }
    }
于 2014-01-15T18:14:34.810 回答
0

本文提供了使用 LOB 的一些背景信息。 检查这个

我刚刚使用 xml 字段测试了 OPENROWSET BULK 语法,它可以工作。如果您的 SQL 服务器有权访问该文件,这是一个选项(我刚刚注意到它不是一个选项,所以我将进行更多实验)。否则,您将需要尝试偏移写入解决方案。到目前为止,我无法让该 .WRITE 方法与 xml 字段一起使用。

本文表明您可以使用 BCP 插入 XML。这将需要安装 SQL Server 客户端工具。

于 2012-12-14T17:57:19.417 回答
0

由于您需要在远程 SQL Server 上存储数据,因此您需要通过网络传输整个文件内容。一般有两种方式:

  • 您将数据传输到远程服务器;
  • 远程服务器从您的本地文件中读取数据。

第二种方式不在这个问题的范围内。假设您在远程服务器上实现存储过程,第一个可以通过多种方式完成:

  • 实现“上传”功能,分部分传输数据,在服务器端加入它们并存储在数据库中;
  • 在客户端打包XML内容,在服务器端解包和存储;
  • 以上方法的组合。
于 2013-07-16T08:53:33.727 回答
0

@xmlstring是输入xml

INSERT INTO @Temptbl           
   SELECT CONVERT(varchar(12), e.query('./Name/text()')) LogNumber             
   FROM @xmlstring.nodes('//RootNode/Root') AS n(e)     

select Temptbl     
于 2013-11-08T11:59:06.333 回答
0

您可以编写存储过程以将大型 xml 文件插入数据库。在该存储过程中,您可以将 xml 文件输入作为 nvarchar(max) 发送。请注意,nvarchar(max) 最多可以接受 8000 个字符。

在您的代码中,主要流程是您从文件流中传递 xml,这是非常昂贵的事情。

如果您的文件比 nvarchar(max) 大,那么您可以在存储过程中使用更多输入参数,然后连接这些输入参数以将单个值插入为 xml 文件。

于 2013-11-21T17:00:38.053 回答
0

将 XML 加载到数据库中的最佳方法是不将 XML 加载到数据库中。如果你真的需要这样做(你可能不需要),你应该看看像 Cassandra 或 Mongo 这样的 noSQL 解决方案。

如果“需要”存储 XML,那么您很可能在其他地方做出了错误的架构选择。考虑是否有更好的方法来表示信息。XML 是一种交换格式,不是长期保存信息的好格式。

话虽如此,您可以使用支持流式传输的 BCP 或 OLEDB 接口。这是一个示例: 批量导入示例

于 2014-01-03T22:19:32.660 回答
0

似乎 SqlXml 参数类型接受 XmlReader 作为输入。

作为基于流的阅读器,这应该消除您遇到的内存限制。

using (SqlCommand cmd = new SqlCommand())
{
    SqlParameter sqlParameter = cmd.CreateParameter();
    sqlParameter.Direction = System.Data.ParameterDirection.Input;
    sqlParameter.Value = new System.Data.SqlTypes.SqlXml(xmlReader);
    ...
}
于 2014-02-21T18:04:13.373 回答
0

我认为您应该创建一个 SSIS 包来将您的 XML 文件导入 SQL Server 数据库。然后,如果您需要使用 C#,您可以通过编程方式执行此包。它会更有效率,它就像一个魅力。

于 2014-06-06T13:02:21.810 回答
0

将 XML 文件存储在 FileTable 中。

于 2014-12-10T13:14:08.577 回答
-1

1)创建一个存储过程以插入表中。
2) 将您的 xml 作为 varchar(max) 从 C# 代码传递。

或者

2)把你的文件分成几块。
3) 以 XML 为参数执行 SP。

于 2014-02-21T07:46:07.347 回答
-1

请使用 sql xml 批量加载程序 http://msdn.microsoft.com/en-us/library/ms171806.aspx

于 2014-06-10T00:56:20.730 回答