2

少し前に、かなり大きなテキスト ファイルを区切って読み取るプログラムを作成しました。プログラムは動作しますが、問題は基本的にコンピューターがフリーズし、完了するまでに時間がかかることです。平均して、各テキスト ファイルには約 10,000 ~ 15,000 行あり、各行は 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 の場合は +1 ですが、INSERT 権限が必要であることに注意してください。私のように、動的 SQL の使用が許可されていない厳密に制御された環境で作業している場合、代わりの方法は、ストアド プロシージャでテーブル値パラメーターを使用することです。そうすれば、レコードのチャンクを渡して、proc に実際の挿入を行わせることができます。

于 2013-06-26T14:01:20.347 に答える
1

SqlBulkCopy一括挿入にはクラスを使用します。

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

時間をわずか数秒に短縮します。

于 2013-06-26T13:55:04.453 に答える
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 に答える
1

前述のように、を使用SqlBulkCopyすると、1 つずつ挿入するよりも高速になりますが、他にも確認できることがあります。

  • テーブルにクラスター化インデックスはありますか? もしそうなら、そのインデックスの真ん中に値を持つ行を挿入しますか? クラスター化されたインデックスの最後に値を追加する方がはるかに効率的です。そうしないと、データを再配置して途中に挿入する必要があるためです (これは CLUSTERED インデックスの場合のみです)。たとえば、クラスター化された主キーとして SSN を使用しているのを見てきました。SSN はランダムに配布されるため、実質的にすべての挿入で物理構造を再配置します。ほとんどの場合、データを最後に挿入する場合 (たとえば、毎日のレコードを追加する場合) は、クラスター化されたキーの一部として日付を使用しても問題ありません。
  • そのテーブルには多くのインデックスがありますか? インデックスを削除してデータを追加し、挿入後にインデックスを再度追加する方が効率的な場合があります。(または、必要のないインデックスを削除するだけです)
于 2013-06-26T14:16:40.943 に答える