1

私は、C# を使用して DataTable 入力を取得し、それを SQL Server テーブルに書き込む最も効率的な方法を見つけることを検討するよう求められました。問題は、ソリューション全体で ODBC 接続を使用する必要があることです。これにより、sqlBulkCopy が除外されます。このソリューションは、SQL Server 2008 R2 までさかのぼるすべての SQL Server バージョンでも機能する必要があります。

次の SQL 構文を使用して、一度に 1000 行のバッチ挿入を使用するのが最善の方法だと考えています。

INSERT INTO dbo.Table1(Field1, Field2) SELECT Value1, Value2 UNION SELECT Value1, Value2

DataTable 入力に対応するテーブルが SQL Server に既に存在するかどうかを確認し、存在しない場合は作成するコードを既に作成しました。

INSERT ステートメント自体を作成するコードも作成しました。私が苦労しているのは、データ テーブルの行から SELECT ステートメントを動的に構築する方法です。行の値にアクセスして SELECT ステートメントを作成するにはどうすればよいですか? 値をシングル クォーテーション (') で囲む必要があるかどうかを判断するために、各列のデータ型も確認する必要があると思います。

これが私の現在のコードです:

        public bool CopyDataTable(DataTable sourceTable, OdbcConnection targetConn, string targetTable)
    {
        OdbcTransaction tran = null;
        string[] selectStatement = new string[sourceTable.Rows.Count];

        // Check if targetTable exists, create it if it doesn't
        if (!TableExists(targetConn, targetTable))
        {
            bool created = CreateTableFromDataTable(targetConn, sourceTable);

            if (!created)
                return false;
        }

        try
        {
            // Prepare insert statement based on sourceTable
            string insertStatement = string.Format("INSERT INTO [dbo].[{0}] (", targetTable);

            foreach (DataColumn dataColumn in sourceTable.Columns)
            {
                insertStatement += dataColumn + ",";
            }

            insertStatement += insertStatement.TrimEnd(',') + ") ";

            // Open connection to target db
            using (targetConn)
            {
                if (targetConn.State != ConnectionState.Open)
                    targetConn.Open();

                tran = targetConn.BeginTransaction();

                for (int i = 0; i < sourceTable.Rows.Count; i++)
                {
                    DataRow row = sourceTable.Rows[i];

                    // Need to iterate through columns in row, getting values and data types and building a SELECT statement

                    selectStatement[i] = "SELECT ";
                }

                insertStatement += string.Join(" UNION ", selectStatement);

                using (OdbcCommand cmd = new OdbcCommand(insertStatement, targetConn, tran))
                {
                    cmd.ExecuteNonQuery();
                }

                tran.Commit();
                return true;
            }
        }       
        catch 
        {
            tran.Rollback();
            return false;
        }
    }

アドバイスをいただければ幸いです。また、私が提案しているものよりも簡単なアプローチがあれば、その詳細は素晴らしいでしょう.

4

2 に答える 2

2

ストアド プロシージャや Bulk Copy は使用できないため、わかりました。数年前にさまざまなアプローチをモデル化したとき、パフォーマンスの重要な決定要因はサーバーへの呼び出しの数でした。そのため、一連の MERGE または INSERT ステートメントをセミコロンで区切られた単一の呼び出しにバッチ処理することが最速の方法であることがわかりました。SQLステートメントをバッチ処理することになりました。SQL ステートメントの最大サイズは 32k だったと思うので、バッチをそのサイズの単位に分割しました。

(注 - 文字列を手動で連結する代わりに StringBuilder を使用してください - パフォーマンスに有益な効果があります)

Psuedo-code
string sqlStatement = "INSERT INTO Tab1 VALUES {0},{1},{2}";
StringBuilder sqlBatch = new StringBuilder();
foreach(DataRow row in myDataTable)
{
    sqlBatch.AppendLine(string.Format(sqlStatement, row["Field1"], row["Field2"], row["Field3"]));
    sqlBatch.Append(";");
}
myOdbcConnection.ExecuteSql(sqlBatch.ToString());

バッチ サイズの複雑さと、文字列置換ステップでの正しいフィールド データ型の書式設定に対処する必要がありますが、それ以外の場合は、これが最高のパフォーマンスになります。

于 2016-08-18T09:26:27.937 に答える