web-dev-qa-db-ja.com

dapper.netを使用して一括挿入を行う最良の方法

SQL Server 2014のテーブルにレコードを挿入するために次のコードを使用しています

using (SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["myConnString"]))
{

   conn.Execute("INSERT statement here", insertList);

}

insertListは、100万個のアイテムが含まれているリストです。この挿入をi5デスクトップでテストしました。同じマシンのSQL Serverに100万のレコードを挿入するのに約65分かかりました。ダッパーが舞台裏で挿入をどのように行っているのかわかりません。データベース接続を100万回開いたり閉じたりしたくありません。

これはdapperで一括挿入を行うための最良の方法ですか、それとも他の方法を試すか、エンタープライズライブラリを使用してプレーンADO.Netを使用する必要がありますか?

[〜#〜]編集[〜#〜]

後から考えると、ADO.Netを使用するほうが優れているので、質問を言い換えます。これがdapperが実行できる最善の方法であるかどうか、またはdapper自体でそれを行うためのより良い方法がないかどうかを知りたいのですが?

11
user20358

パフォーマンスが必要な場合は、Dapperを使用して挿入するのではなく、SqlBulkCopyを使用することをお勧めします。パフォーマンスの比較については、こちらをご覧ください: http://www.ikriv.com/dev/db/SqlInsert/SqlInsert.html

14
Alex Marshall

Ehsan Sajjadのコメントに基づいて、方法の1つは、 ser-defined TABLE type のREADONLYパラメータを持つストアドプロシージャを記述することです。

姓と名で構成される連絡先を一括挿入する場合は、次のようにします。1)テーブルタイプを作成します。

CREATE TYPE [dbo].[MyTableType] AS TABLE(
    [FirstName] [varchar](50) NULL,
    [LastName] [varchar](50) NULL
)
GO

2)次に、上記のテーブルタイプを使用するストアドプロシージャを作成します。

CREATE PROC [dbo].[YourProc]
/*other params here*/
@Names AS MyTableType READONLY
AS
/* proc body here 
 */
GO

3).NET側で、パラメーターをSystem.Data.SqlDbType.Structuredとして渡します。これには通常、メモリ内データテーブルを作成し、そこに行を追加して、このDataTableオブジェクトを@Namesパラメーターとして使用します。注:DataTableはメモリを集中的に使用するものと見なされます。コードをプロファイリングして、サーバーでリソースの問題が発生しないことを確認してください。

代替ソリューションここで概説されているアプローチを使用してください: https://stackoverflow.com/a/9947259/190476 ソリューションはDELETEですが、挿入または更新にも適用できます。

7

優れたパフォーマンスで挿入するための最良の無料の方法は、AlexとAndreasが提案したようにSqlBulkCopyクラスを直接使用することです。

免責事項:私はプロジェクトのオーナーです Dapper Plus

このプロジェクトは無料ではありませんが、次の操作をサポートしています。

  • 一括挿入
  • BulkUpdate
  • BulkDelete
  • BulkMerge

マッピングを使用して、ID列のような値を出力できるようにする。

// CONFIGURE & MAP entity
DapperPlusManager.Entity<Order>()
                 .Table("Orders")
                 .Identity(x => x.ID);

// CHAIN & SAVE entity
connection.BulkInsert(orders)
          .AlsoInsert(order => order.Items);
          .Include(x => x.ThenMerge(order => order.Invoice)
                         .AlsoMerge(invoice => invoice.Items))
          .AlsoMerge(x => x.ShippingAddress);   
3
Jonathan Magnan

最初の選択はSQL一括コピーにする必要があります。SQLインジェクションから安全であるためです。

ただし、パフォーマンスを大幅に改善する方法があります。複数の挿入を1つのSQLにマージし、複数ではなく1つの呼び出しのみを使用できます。したがって、これの代わりに:

enter image description here

あなたはこれを持つことができます:

enter image description here

ユーザーを一括で挿入するためのコードは次のようになります。

public async Task InsertInBulk(IList<string> userNames)
{
    var sqls = GetSqlsInBatches(userNames);
    using (var connection = new SqlConnection(ConnectionString))
    {
        foreach (var sql in sqls)
        {
            await connection.ExecuteAsync(sql);
        }
    }
}

private IList<string> GetSqlsInBatches(IList<string> userNames)
{
    var insertSql = "INSERT INTO [Users] (Name, LastUpdatedAt) VALUES ";
    var valuesSql = "('{0}', getdate())";
    var batchSize = 1000;

    var sqlsToExecute = new List<string>();
    var numberOfBatches = (int)Math.Ceiling((double)userNames.Count / batchSize);

    for (int i = 0; i < numberOfBatches; i++)
    {
        var userToInsert = userNames.Skip(i * batchSize).Take(batchSize);
        var valuesToInsert = userToInsert.Select(u => string.Format(valuesSql, u));
        sqlsToExecute.Add(insertSql + string.Join(',', valuesToInsert));
    }

    return sqlsToExecute;
}

記事全体とパフォーマンスの比較はこちらから入手できます: http://www.michalbialecki.com/2019/05/21/bulk-insert-in-dapper/

0
Mik