3

特定のデータ テーブルのすべての行をデータベースに保存するメソッドを C# で記述する必要がありますが、各行の行状態に依存することはできません。データベース テーブルのデータを大規模にループしたり、一定のデータベース クエリを実行したりせずに、これを行う最善の方法を考えることはできません。

基本的に、データテーブルの各行に対して次の挿入/更新を実行したいのですが、より効率的です:

INSERT INTO table
(   col1,
    col2    )
SELECT
    'value1',
    'value2'
FROM dual
WHERE NOT EXISTS ( SELECT * FROM table WHERE col1 = 'value1' );

UPDATE table
SET col2 = 'value2'
WHERE col1 = 'value1';

このような構造が最も効率的であると考えていました。

private void SaveDataTable(DataTable dataTable)
{
    //Remove data from the dataTable where it already exists in the database

    //Determine if the values are to be inserted or updated

    //Save relevant data
}

これを達成するための最善の方法についてのアイデアはありますか?

4

2 に答える 2

1

アプリケーションの観点からアプローチするのではなく、データベースにこれを管理させることを強くお勧めします。SQL Serverはセット操作用に最適化されており、基本的に2つのセット操作を実行する必要があります。

  • テーブルに存在するすべてのレコードを更新します。これは、DataTable
  • DataTableテーブルに存在しないすべてのレコードを挿入します

そのためには、新しいテーブルを作成することをお勧めしますtableStaging

create table tableStaging (
    batchId uniqueidentifier not null, col1 int not null, col2 int not null)

いくつかの重要なポイント:

  • このbatchId列を使用すると、アプリケーションのさまざまな呼び出し元によって同時に実行されている複数の操作を識別できます。この値は、各レコードではなく、この操作を実行しているセットごとに1回生成する必要があります。
  • このbatchId列は、構造体uniqueidentifierに変換され、を呼び出すことでクライアント側で簡単に生成できるためです。GuidGuid.NewGuid
  • 今後の操作をより効率的にするためbatchIdに、テーブルの主キーとすべての主キーで構成される複合主キーが必要になる場合があります。table

これを行ったら、SqlBulkCopyクラスを使用してからレコードを一括挿入しますDataTableインスタンス取得するオーバーロードがWriteToServerDataTableあります)。

最後に、レコードがクラスに入ると、2つのテーブルの間に列と主キーが存在するすべてのアイテムに対して、ステージングテーブルには存在し、ステージングテーブルには存在しないアイテムの挿入tableStagingを実行するストアドプロシージャを呼び出すことができます。ターゲットテーブル。または、SQL Server 2008を使用している場合は、ステートメントを使用して両方を一度に実行できます。updatebatchIdmerge

次に、ステージングテーブルをクリーンアップするだけです。batchIdストアドプロシージャを呼び出したりコマンドを実行したりできるので、メインテーブルの更新が完了したらレコードを削除できます。または、誰もこれを使用しないことがわかった時点まで待って、を呼び出すことができますtruncate table。テーブル(ステージングテーブルには外部キーが含まれていてはならないtruncate tableため、正常に機能するはずです)。

于 2012-06-05T01:26:22.367 に答える
0

指定されたデータテーブルの各行のクエリを回避することはできませんが、これは今のところうまくいくと思います。ご回答ありがとうございます。それらはすべて非常に役に立ちました。

(Oracle 固有):

internal override bool RecordExists(DataRow row)
{
    string query = string.Concat("SELECT 1 FROM ", row.Table.TableName, " ", GetDbTableFilter(row, row.Table.PrimaryKey), " AND ROWNUM = 1");

    using(OracleCommand cmd = new OracleCommand(query, (OracleConnection)_connectionLibrary.CurrentConnection))
        using(OracleDataReader reader = cmd.ExecuteReader())
            return (reader != null && reader.Read());
}

internal override string GetDbTableFilter(DataRow row, DataColumn[] columns)
{
    string filter = "";

    foreach (DataColumn column in columns)
    {
        if (!string.IsNullOrEmpty(filter))
            filter += " AND ";

        if (string.IsNullOrEmpty(row[column].ToString()))
        {
            filter += string.Concat("(", column.ColumnName, " IS NULL OR TRIM(", column.ColumnName, ") = '')");
            continue;
        }

        if (column.DataType.Equals(typeof(DateTime)))
        {
            filter += string.Concat(column.ColumnName, " = TO_DATE('", row[column].ToString(), "')");
            continue;
        }

        filter += string.Concat(column.ColumnName, " = '", row[column].ToString(), "'");
    }

    return string.Concat("WHERE ", filter);
}

public override void SaveDataTable(DataTable dataTable)
{
    dataTable.AcceptChanges();

    foreach (DataRow row in dataTable.Rows)
        if(RecordExists(row))
            row.SetModified();
        else
            row.SetAdded();

    using (OracleDataAdapter adapter = new OracleDataAdapter(string.Format("SELECT * FROM {0}", dataTable.TableName), (OracleConnection)_connectionLibrary.CurrentConnection))
        using (OracleCommandBuilder commandBuilder = new OracleCommandBuilder(adapter))
        {
            commandBuilder.SetAllValues = true;
            commandBuilder.ConflictOption = ConflictOption.OverwriteChanges;

            adapter.InsertCommand = commandBuilder.GetInsertCommand(true);
            adapter.UpdateCommand = commandBuilder.GetUpdateCommand(true);

            adapter.Update(dataTable);
        }
}
于 2012-06-05T21:22:12.047 に答える