2

前段时间我写了一个程序来分隔和读取相当大的文本文件。该程序可以运行,但问题是它基本上会冻结计算机并且需要很长时间才能完成。每个文本文件平均有大约 10K 到 15K 行,每行代表 SQL 表中的一个新行。

我的程序的工作方式是我首先读取所有行(这是进行定界的地方)并将它们存储在数组中,然后我遍历每个数组元素并将它们插入 SQL 表中。这一切都是一次完成的,我怀疑正在消耗大量内存,导致程序冻结计算机。

这是我读取文件的代码:

private void readFile()
    {
        //String that will hold each line read from the file
        String line;

        //Instantiate new stream reader 
        System.IO.StreamReader file = new System.IO.StreamReader(txtFilePath.Text);

        try
        {
            while (!file.EndOfStream)
            {
                line = file.ReadLine();

                if (!string.IsNullOrWhiteSpace(line))
                {
                    if (this.meetsCondition(line))
                    {
                        badLines++;
                        continue;
                    } // end if

                    else
                    {
                        collection.readIn(line);
                        counter++;
                    } // end else
                } // end if

            } // end while

            file.Close();

        } // end try

        catch (Exception exceptionError)
        {
            //Placeholder
        }

插入代码:

for (int i = 0; i < counter; i++)
            {
                //Iterates through the collection array starting at first index and going through until the end
                //and inserting each element into our SQL Table
                //if (!idS.Contains(collection.getIdItems(i)))
                //{
                    da.InsertCommand.Parameters["@Id"].Value = collection.getIdItems(i);
                    da.InsertCommand.Parameters["@Date"].Value = collection.getDateItems(i);
                    da.InsertCommand.Parameters["@Time"].Value = collection.getTimeItems(i);
                    da.InsertCommand.Parameters["@Question"].Value = collection.getQuestionItems(i);
                    da.InsertCommand.Parameters["@Details"].Value = collection.getDetailsItems(i);
                    da.InsertCommand.Parameters["@Answer"].Value = collection.getAnswerItems(i);
                    da.InsertCommand.Parameters["@Notes"].Value = collection.getNotesItems(i);
                    da.InsertCommand.Parameters["@EnteredBy"].Value = collection.getEnteredByItems(i);
                    da.InsertCommand.Parameters["@WhereReceived"].Value = collection.getWhereItems(i);
                    da.InsertCommand.Parameters["@QuestionType"].Value = collection.getQuestionTypeItems(i);
                    da.InsertCommand.Parameters["@AnswerMethod"].Value = collection.getAnswerMethodItems(i);
                    da.InsertCommand.Parameters["@TransactionDuration"].Value = collection.getTransactionItems(i);
                    da.InsertCommand.ExecuteNonQuery();
                //}

                //Updates the progress bar using the i in addition to 1 
                _worker.ReportProgress(i + 1);

            } // end for
4

5 回答 5

2

如果您可以将您的集合映射到 DataTable,那么您可以使用SqlBulkCopy来导入您的数据。SqlBulkCopy 是将数据从 .Net 导入 SqlServer 的最快方法。

于 2013-06-26T13:55:12.740 回答
1

如前所述,使用SqlBulkCopy会比逐个插入要快,但您还可以查看其他内容:

  • 表上有聚集索引吗?如果是这样,您是否会在该索引的中间插入具有值的行?在聚集索引的末尾添加值效率更高,否则它将不得不重新排列数据以插入中间(这仅适用于聚集索引)。在示例中,我看到我们使用 SSN 作为集群主键。由于 SSN 将随机分布,因此您几乎要重新排列每个插入的物理结构。如果您主要在末尾插入数据(例如添加每日记录),则将日期作为集群键的一部分可能没问题
  • 那张表上有很多索引吗?删除索引、添加数据并在插入后重新添加索引可能更有效。(或者只是删除你不需要的索引)
于 2013-06-26T14:16:40.943 回答
1

使用SqlBulkCopy类进行批量插入。

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx

您将把时间缩短到几秒钟。

于 2013-06-26T13:55:04.453 回答
1

正如其他人所说,为 SqlBulkCopy +1,但请注意它需要 INSERT 权限。如果您像我一样在严格控制的环境中工作,不允许您使用动态 SQL,另一种方法是让您的存储过程使用表值参数。这样,您仍然可以传递大量记录并让 proc 进行实际插入。

于 2013-06-26T14:01:20.347 回答
1

作为一个示例如何使用 SqlBulkCopy 类的功能,(只是呈现想法的伪代码)

首先将您的集合类更改为托管内部 DataTable,并在构造函数中定义您的 readIn 方法使用的架构

public class MyCollection
{
     private DataTable loadedData = null;
     public MyCollection()
     {
         loadedData = new DataTable();
         loadedData.Columns.Add("Column1", typeof(string));
         .... and so on for every field expected
     }

     // A property to return the collected data
     public DataTable GetData
     {
        get{return loadedData;}
     }

     public void readIn(string line)
     {
          // split the line in fields
          DataRow r = loadedData.NewRow();
          r["Column1"] = splittedLine[0];
          .... and so on
          loadedData.Rows.Add(r);
     }
}

最后是将数据上传到您的服务器的代码

using (SqlConnection connection = new SqlConnection(connectionString))
{
   connection.Open();
   using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
   {
      bulkCopy.DestinationTableName = "destinationTable";
      try
      {
         bulkCopy.WriteToServer(collection.GetData());
      }
      catch (Exception ex)
      {
         Console.WriteLine(ex.Message);
      }
   }
}
于 2013-06-26T14:02:55.973 回答