2016-08-18 128 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,值 UNION SELECT Value1,Value2

我已經編寫了代碼,檢查SQL Server上是否存在與DataTable輸入對應的表,如果沒有,則創建一個。

我也寫了代碼來創建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,就是這個。任何涉及存儲過程和/或TVP的解決方案都不在我的範圍內。 –

回答

2

好吧,因爲我們不能使用存儲過程或批量複製;當我在幾年前對各種方法進行建模時,性能的關鍵決定因素是對服務器的調用數量。因此,將一組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()); 

您需要處理批量大小的併發症,並在字符串中正確的字段的數據類型的格式更換步驟,但否則這將是最佳性能。

+0

您是否能夠共享您用於批量生成SQL語句的代碼?那就是我目前被卡住的地方。 –

+0

非常感謝,很好。 –

1

PhillipH的標記解決方案是開放的幾個錯誤和SQL注入。

通常情況下,您應該使用參數構建一個DbCommand並執行此操作,而不是執行自建SQL語句。

對於ODBC和OLEDB,CommandText必須爲"INSERT INTO Tab1 VALUES ?,?,?",SqlClient需要命名參數(「@ <名稱>」)。

參數應添加與下墊柱的尺寸。