0

我正在寻找是否可以采取一些措施来显着加快 ADO.net 插入,但仍然使用多个INSERT INTO table values (...)命令进行插入。我正在将专有的、不可查询的数据库文件中的数据暂存到 SQL Server 中。我正在编写的实用程序将从脚本中使用。

我意识到为了获得最佳性能,INSERT INTO追求的路线是错误的,但我仍然想知道是否应该尝试一些 ADO.net 或 SQL Server 方法。

我正在使用 C# 2010 连接到 SQL Server 2008。我将我的性能与使用 Microsoft 的 ODBC 3.5 驱动程序和 Native Client 10.0 将数据插入 SQL Server 的非托管代码进行比较。期望能够匹配 ODBC 的性能是否合理?

目标表没有索引或其他约束、触发器,它只是一个临时表。源数据是一堆非标准数据类型,我必须将它们按摩成字符串。

剥离到相关细节,我的代码是:

SqlCommand comm = new SqlCommand;
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
SqlTransaction insertTransaction = conn.BeginTransaction();
comm.Connection = conn;
comm.Transaction = insertTransaction;

while(buffer.ReadNext())  // fill a buffer that I use to make my query
{
    // form my insert statement and assign it
    // It looks like: INSERT INTO myTable VALUES (5,'2016-02-16',NULL,3)
    // A good fraction of the data is numeric with decimal points. A good
    // fraction is dates.  The parsing and string building,
// extravagantly inefficient as it is, is not the culprit.
    // The  INSERT INTO does not specify the column names

    comm.CommandText = myStatement;
    if (comm.ExecuteNonQuery() != 1) {throw...}
}

insertTransaction.commit;

我试过指定不同的隔离级别;我无法指定.Snapshot(不想配置目标数据库以允许它)。没有什么太大的不同。

如果我注释掉comm.ExecuteNoQuery让它通过形成 INSERT 语句旋转,它会按照我认为的速度进行,如果它真的在做某事的话。如果我取消注释,它需要的时间大约是我认为应该花费的 8 倍。“‘八倍长’从何而来?” 你问。好吧,我用 Pervasive Data Integrator(ye olde Data Junction)做了一个类似的操作(端到端的相同操作)。从 Pervasive 崩溃时的诊断来看,我相信该程序正在使用连续INSERT INTO 语句进行插入,就像我一样。INSERT INTO它比我的程序快大约 8 倍,如果我不执行它们 ,它只比形成我的语句要慢一点。

Pervasive Integrator 正在通过 ODBC 添加,并且 ODBC 驱动程序设置为使用 Sql Native 客户端。而且 Data Integrator 不是 .Net 软件。我实际上并没有尝试通过 ODBC,我想我现在会尝试它,但我的目标是摆脱 ODBC,因此它只是一个数据点,而不是解决方案,即使它更快。

我尝试填充数据表以进行批量插入,但填充数据集也花费了太长时间。我认为使用批量插入的一些替代方法是使其工作的最快方法,但即使我最终停止使用我的INSERT INTO方法,我也很好奇为什么它需要这么长时间(比我认为的要长)

插入语句相当长,我的表中有大约 350 列。

4

2 回答 2

1

您是否尝试过将您的插入值组合成简单的东西:

INSERT INTO myTables VALUES (5,'2016-02-16',NULL,3), (6,'2015-02-16',NULL,6), (7,'2012-02-16',NULL,6)...

这是一个简单的实现,它并不优雅(而且它未经测试,我在记事本中写了这个,所以很可能有错别字),但是......

SqlCommand comm = new SqlCommand;
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
SqlTransaction insertTransaction = conn.BeginTransaction();
comm.Connection = conn;
comm.Transaction = insertTransaction;

String baseQuery = "INSERT INTO myTable VALUES ";

List<String> values = new List<String>();

Int32 i = 0;

while(buffer.ReadNext())  // fill a buffer that I use to make my query
{
    // Build your VALUES section here
    values.Add("(5,'2016-02-16',NULL,3)");

    if (i % 100 == 0)  // Chunk these every 100
    {
        myStatement = baseQuery + String.Join(", ", values.ToArray());

        comm.CommandText = myStatement;

        if (comm.ExecuteNonQuery() != 1) {throw...}

        insertTransaction.commit;

        values = new List<String>();  // Clear out our values and start a new
    }

    i++;
}

if (values.Count > 0)  // If any are left, INSERT them
{
    myStatement = baseQuery + String.Join(", ", values.ToArray());

    comm.CommandText = myStatement;

   if (comm.ExecuteNonQuery() != 1) {throw...}

   insertTransaction.commit;
}
于 2012-03-13T16:32:12.960 回答
0
  1. 数据是否一次排成一行?换句话说,您是否让用户将数据输入应用程序并按下按钮来启动插入?
    或者
  2. 是否所有数据都可以随时插入?

如果(1.)只需按照您一直在做的方式进行 INSERT INTO 。

我反对在应用程序/客户端一次累积一行数据。这会导致很多问题。

如果 (2.) 进行批量导入。

有 3 种类型的批量导入和批量导出操作
http://msdn.microsoft.com/en-us/library/ms187042.aspx

这些文件是文本格式的吗?
它们是柱状的吗?
它们是否可以从文件系统(即 C:\pathTo\file.ext)访问?
如果是,生成一个 bcp 格式文件(从程序中打印出来,注意包含 C:\pathTo\file.ext)。
然后 TRUNCATE 临时表(或上面生成的格式文件中的 TRUNCATE INTO)并通过 system() 调用调用 bcp 实用程序,将格式文件和数据库连接作为参数。
它超级快。
直接 bcp 上传将击败您可以通过 ado.net 上传的任何内容。

于 2012-03-13T16:51:15.580 回答