web-dev-qa-db-ja.com

200万行をSQL​​ Serverにすばやく挿入する

テキストファイルから約200万行を挿入する必要があります。

挿入では、いくつかのマスターテーブルを作成する必要があります。

このような大量のデータセットをSQL Serverに挿入する最良かつ高速な方法は何ですか?

66
Wadhawan Vishal

SqlBulkCopy classで試すことができます。

SQL Serverテーブルに別のソースからのデータを効率的に一括ロードできます。

クールな方法があります ブログ投稿 使用方法について。

52
Soner Gönül
  1. DataSetのテキストファイルのデータを読む方が良いと思います

  2. 試すSqlBulkCopy- C#アプリからSQLへの一括挿入

    // connect to SQL
    using (SqlConnection connection = new SqlConnection(connString))
    {
        // make sure to enable triggers
        // more on triggers in next post
        SqlBulkCopy bulkCopy = new SqlBulkCopy(
            connection, 
            SqlBulkCopyOptions.TableLock | 
            SqlBulkCopyOptions.FireTriggers | 
            SqlBulkCopyOptions.UseInternalTransaction,
            null
            );
    
        // set the destination table name
        bulkCopy.DestinationTableName = this.tableName;
        connection.Open();
    
        // write the data in the "dataTable"
        bulkCopy.WriteToServer(dataTable);
        connection.Close();
    }
    // reset
    this.dataTable.Clear();
    

または

上部のステップ1を実行した後

  1. DataSetからXMLを作成する
  2. XMLをデータベースに渡し、一括挿入を行う

この記事で詳細を確認できます。 C#DataTableとSQLサーバーOpenXML関数を使用したデータの一括挿入

ただし、200万レコードでテストされていないため、200万レコードをロードして挿入する必要があるため、マシンのメモリを消費します。

66
Pranay Rana

SqlBulkCopyのソリューションを再:

StreamReaderを使用して、テキストファイルを変換および処理しました。結果は私のオブジェクトのリストでした。

DatatableまたはList<T>とバッファサイズ(CommitBatchSize)を取るクラスを作成しました。拡張機能(2番目のクラス)を使用して、リストをデータテーブルに変換します。

非常に高速に動作します。私のPCでは、10秒未満で1,000万件を超える複雑なレコードを挿入できます。

クラスは次のとおりです。

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DAL
{

public class BulkUploadToSql<T>
{
    public IList<T> InternalStore { get; set; }
    public string TableName { get; set; }
    public int CommitBatchSize { get; set; }=1000;
    public string ConnectionString { get; set; }

    public void Commit()
    {
        if (InternalStore.Count>0)
        {
            DataTable dt;
            int numberOfPages = (InternalStore.Count / CommitBatchSize)  + (InternalStore.Count % CommitBatchSize == 0 ? 0 : 1);
            for (int pageIndex = 0; pageIndex < numberOfPages; pageIndex++)
                {
                    dt= InternalStore.Skip(pageIndex * CommitBatchSize).Take(CommitBatchSize).ToDataTable();
                BulkInsert(dt);
                }
        } 
    }

    public void BulkInsert(DataTable dt)
    {
        using (SqlConnection connection = new SqlConnection(ConnectionString))
        {
            // make sure to enable triggers
            // more on triggers in next post
            SqlBulkCopy bulkCopy =
                new SqlBulkCopy
                (
                connection,
                SqlBulkCopyOptions.TableLock |
                SqlBulkCopyOptions.FireTriggers |
                SqlBulkCopyOptions.UseInternalTransaction,
                null
                );

            // set the destination table name
            bulkCopy.DestinationTableName = TableName;
            connection.Open();

            // write the data in the "dataTable"
            bulkCopy.WriteToServer(dt);
            connection.Close();
        }
        // reset
        //this.dataTable.Clear();
    }

}

public static class BulkUploadToSqlHelper
{
    public static DataTable ToDataTable<T>(this IEnumerable<T> data)
    {
        PropertyDescriptorCollection properties =
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        return table;
    }
}

}

カスタムオブジェクトList<PuckDetection>ListDetections)のリストを挿入する場合の例を次に示します。

var objBulk = new BulkUploadToSql<PuckDetection>()
{
        InternalStore = ListDetections,
        TableName= "PuckDetections",
        CommitBatchSize=1000,
        ConnectionString="ENTER YOU CONNECTION STRING"
};
objBulk.Commit();

BulkInsertクラスを変更して、必要に応じて列マッピングを追加できます。たとえば、最初の列としてIDキーがあります(これは、データテーブルの列名がデータベースと同じであると仮定しています)

//ADD COLUMN MAPPING
foreach (DataColumn col in dt.Columns)
{
        bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
23
Amir

Bcpユーティリティを使用します。 (一括コピープログラム)毎月約150万件のテキストレコードを読み込みます。各テキストレコードの幅は800文字です。私のサーバーでは、SQL Serverテーブルに150万件のテキストレコードを追加するのに約30秒かかります。

Bcpの手順は http://msdn.Microsoft.com/en-us/library/ms162802.aspx にあります

4
Bill Edmett

私は最近このシナリオに遭遇し(700万行をはるかに上回り)、一度に5,000のセグメントで(生データをSQL挿入ステートメントに解析した後)powershell経由でsqlcmdを使用して強化しました(SQLは一括ジョブで700万行を処理できませんSQL Server 2012 Enterpriseで新しいシーケンスコマンドを活用する必要があったので、5Kの小さな断片に分割しない限り、500,000行です。上記のシーケンスコマンドを使用して700万行のデータを迅速かつ効率的に挿入するプログラム的な方法を見つけることができませんでした。

第二に、100万行以上のデータを一度に挿入する際に注意すべき点の1つは、挿入プロセス中のCPUとメモリの消費(主にメモリ)です。 SQLは、上記のプロセスを解放せずに、この規模のジョブでメモリ/ CPUを使い果たします。言うまでもなく、サーバーに十分な処理能力やメモリがない場合は、短時間で非常に簡単にクラッシュする可能性があります(難しい方法を見つけました)。メモリの消費量が70〜75%を超えた時点でサーバーを再起動すると、プロセスが解放されて通常の状態に戻ります。

最終的な実行計画を実際に作成する前に、サーバーの制限が何であるか(使用するCPU /メモリリソースが制限されている場合)を確認するために、一連の試行錯誤テストを実行する必要がありました。これを実稼働環境に展開する前に、テスト環境で同じことを行うことをお勧めします。

3
Techie Joe

この方法を試してみたところ、データベース挿入の実行時間が大幅に短縮されました。

List<string> toinsert = new List<string>();
StringBuilder insertCmd = new StringBuilder("INSERT INTO tabblename (col1, col2, col3) VALUES ");

foreach (var row in rows)
{
      // the point here is to keep values quoted and avoid SQL injection
      var first = row.First.Replace("'", "''")
      var second = row.Second.Replace("'", "''")
      var third = row.Third.Replace("'", "''")

      toinsert.Add(string.Format("( '{0}', '{1}', '{2}' )", first, second, third));
}
if (toinsert.Count != 0)
{
      insertCmd.Append(string.Join(",", toinsert));
      insertCmd.Append(";");
}
using (MySqlCommand myCmd = new MySqlCommand(insertCmd.ToString(), SQLconnectionObject))
{
      myCmd.CommandType = CommandType.Text;
      myCmd.ExecuteNonQuery();
}

* SQL接続オブジェクトを作成し、SQLconnectionObjectを記述した場所に置き換えます。

0
Amey Vartak