web-dev-qa-db-ja.com

C#での一括更新

データベースに大量のデータを挿入するために、私はすべての挿入情報をリストに収集し、このリストをDataTableに変換していました。次に、SqlBulkCopyを介してそのリストをデータベースに挿入します。

生成されたリストの送信先
LiMyList
データベースに挿入するすべてのバルクデータの情報が含まれています
それを一括挿入操作に渡します

InsertData(LiMyList, "MyTable");

InsertData

 public static void InsertData<T>(List<T> list,string TableName)
        {
                DataTable dt = new DataTable("MyTable");
                clsBulkOperation blk = new clsBulkOperation();
                dt = ConvertToDataTable(list);
                ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
                {
                    bulkcopy.BulkCopyTimeout = 660;
                    bulkcopy.DestinationTableName = TableName;
                    bulkcopy.WriteToServer(dt);
                }
        }    

public static DataTable ConvertToDataTable<T>(IList<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;
        }

今、更新操作を行いたいのですが、データを挿入するための方法は、C#.Netからデータベースにデータを更新するためのSqlBulkCopyによって行われます

55
Co. Aden

以前に行ったことは、データから一時テーブルへの一括挿入を実行し、コマンドまたはストアドプロシージャを使用して、一時テーブルを宛先テーブルに関連付けるデータを更新することです。一時テーブルは追加の手順ですが、データを行ごとに更新する場合と比較して、行の量が多い場合は、一括挿入と大量更新でパフォーマンスを向上させることができます。

例:

public static void UpdateData<T>(List<T> list,string TableName)
{
    DataTable dt = new DataTable("MyTable");
    dt = ConvertToDataTable(list);

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
    {
        using (SqlCommand command = new SqlCommand("", conn))
        {
            try
            {
                conn.Open();

                //Creating temp table on database
                command.CommandText = "CREATE TABLE #TmpTable(...)";
                command.ExecuteNonQuery();

                //Bulk insert into temp table
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn))
                {
                    bulkcopy.BulkCopyTimeout = 660;
                    bulkcopy.DestinationTableName = "#TmpTable";
                    bulkcopy.WriteToServer(dt);
                    bulkcopy.Close();
                }

                // Updating destination table, and dropping temp table
                command.CommandTimeout = 300;
                command.CommandText = "UPDATE T SET ... FROM " + TableName + " T INNER JOIN #TmpTable Temp ON ...; DROP TABLE #TmpTable;";
                command.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                // Handle exception properly
            }
            finally
            {
                conn.Close();
            }
        }
    }
}

一時テーブルのスコープは接続ごとであるため、各ステップで一時テーブルを使用できるようにするために、単一の接続が操作全体の実行に使用されることに注意してください。

66

私の個人的な経験では、このような状況に対処する最良の方法は、Table-Valued ParameterUser-Defined Table Typeを持つストアドプロシージャを利用することです。データテーブルの列を使用して型を設定し、said-dataテーブルをSQLコマンドのパラメーターとして渡すだけです。

ストアドプロシージャ内で、いくつかの一意のキーに直接結合できます(更新するすべての行が存在する場合)、または-更新と挿入の両方を行う必要がある状況に遭遇した場合は、SQL Mergeコマンドを使用します必要に応じて、更新と挿入の両方を処理するストアドプロシージャ。

Microsoftには、Merge用の 構文リファレンス例の記事 の両方があります。

.NETピースの場合、パラメータタイプをSqlDbType.Structuredに設定し、said-parameterの値を、更新するレコードを含むデータテーブルに設定するだけです。

この方法には、明快さとメンテナンスの容易さの両方の利点があります。パフォーマンスを向上させる方法があるかもしれませんが(一時テーブルにドロップしてからそのテーブルを反復するなど)、. NETとSQLにテーブルの転送とレコード自体の更新を処理させるという単純さよりも重要だと思います。キッス。

36
Locke

Nugetで利用可能なSqlBulkToolsを試してください。

免責事項:私はこのライブラリの著者です。

var bulk = new BulkOperations();
var records = GetRecordsToUpdate();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<MyTable>()
            .ForCollection(records)
            .WithTable("MyTable")
            .AddColumn(x => x.SomeColumn1)
            .AddColumn(x => x.SomeColumn2)
            .BulkUpdate()
            .MatchTargetOn(x => x.Identifier)
            .Commit(conn);
    }

    trans.Complete();
}  

「SomeColumn1」と「SomeColumn2」のみが更新されます。より多くの例を見つけることができます here

3
Greg R Taylor

一時テーブルに新しい値を挿入してから、次のような宛先テーブルに対してマージを実行します。

MERGE [DestTable] AS D 
USING #SourceTable S
    ON D.ID = S.ID
WHEN MATCHED THEN 
    UPDATE SET ...
WHEN NOT MATCHED 
THEN INSERT (...) 
VALUES (...);
2
aggaton

アーカイブするポイントがわからない...あなたの質問がテーブルコンテンツ全体をすばやく置き換えることに関するものである場合は、truncatehttp://technet.Microsoft.com/ en-us/library/ms177570.aspx )およびデータの新しい部分の一括挿入。ただし、この方法は、外部キーの制約がない場合にのみ機能します。

GuillermoGutiérrezからの回答 を探すよりも実際に更新したい場合。

2
Yaugen Vlasau

すべてのデータを含むクエリを作成してみてください。 caseを使用します。こんな感じ

update your_table
set some_column = case when id = 1 then 'value of 1'
                       when id = 5 then 'value of 5'
                       when id = 7 then 'value of 7'
                       when id = 9 then 'value of 9'
                  end
where id in (1,5,7,9)
1
juergen d

私はTempTableアプローチに行きます。なぜなら、その方法では何もロックされないからです。しかし、ロジックをフロントエンドのみに配置する必要があり、一括コピーを使用する必要がある場合は、次のような整合性を確保するために同じSqlTransactionでDelete/Insertアプローチを試みます。

// ...

dt = ConvertToDataTable(list);

using (SqlConnection cnx = new SqlConnection(myConnectionString))
{
    using (SqlTranscation tran = cnx.BeginTransaction())
    {
        DeleteData(cnx, tran, list);

        using (SqlBulkCopy bulkcopy = new SqlBulkCopy(cnx, SqlBulkCopyOptions.Default, tran))
        {
            bulkcopy.BulkCopyTimeout = 660;
            bulkcopy.DestinationTableName = TabelName;
            bulkcopy.WriteToServer(dt);
        }

        tran.Commit();
    }
}
1
Mauro2