2016-08-18 30 views
1

DataTable入力を取得してC#を使用してSQL Serverテーブルに書き込む最も効率的な方法を見つけることを検討してきました。この問題を解決するには、ソリューション全体でODBC接続を使用する必要がありますが、これはsqlBulkCopyを排除します。このソリューションは、SQL Server 2008 R2以降のすべてのSQL Serverバージョンでも動作する必要があります。ODBC接続を使用してDataTableからSQL Serverテーブルに一括挿入

私は最善のアプローチは、次のSQL構文を使用して、一度に1000行のバッチ挿入を使用するだろうと考えています:

INSERT INTO dbo.Table1(フィールド1、フィールド2) SELECT値1、値2 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; 
     } 
    } 

は、ここに私の現在のコードです。また、私が示唆しているものよりも簡単なアプローチがあるなら、その詳細はすばらしいでしょう。

+0

ADO.netのテーブルパラメータも除外していますか?テーブルパラメータをストアドプロシージャに渡してから、受信テーブルでMERGEコマンドを使用することはこれを達成するための最も効率的な方法です。 – PhillipH

+0

問題は、私たちが書いているSQL Serverに対して非常に限られた権利を持つことです。私たちはテーブルにCRUDを持っています。それはそれです。保存されたprocsおよび/またはTVPを含むあらゆる解決策は、私が恐れている範囲ではない。 –

答えて

2

私たちはストアドプロシージャまたはバルクコピーを使用できないため、Okです。私が数年前にさまざまなアプローチをモデル化したとき、パフォーマンスへの重要な決定要因はサーバーへの呼び出し数でした。したがって、セミコロンで区切られた単一の呼び出しに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()); 

あなたはバッチサイズの合併症に対処する必要があり、文字列内の正しいフィールドのデータ型のフォーマット-replaceステップを実行します。それ以外の場合は、これが最高のパフォーマンスになります。

+0

バッチでSQL文を作成するために使用したコードを共有できますか?それは私が現在立ち往生している場所です。 –

+0

多くのおかげで、tthatの素晴らしいです。 –

1

PhillipHのマーク付きソリューションは、いくつかの間違いやSQLインジェクションのために公開されています。

通常、パラメータ付きのDbCommandをビルドし、自己ビルドSQLステートメントを実行する代わりにこれを実行する必要があります。

ODBCとOLEDBのCommandTextは"INSERT INTO Tab1 VALUES ?,?,?"である必要があります.SqlClientには名前付きパラメータ( "@ <Name>")が必要です。

パラメータは、下敷きの列の寸法で追加する必要があります。

関連する問題