2010-09-24 47 views
7

我使用SqlBulkCopy針對具有不同列集的兩個SQL Server 2008(將一些數據從prod服務器移動到dev)。所以想跳過一些尚未存在/尚未刪除的列。跳過SqlBulkCopy中的一些列

我該怎麼做?一些技巧ColumnMappings

編輯:

我下一步:

DataTable table = new DataTable(); 
using (var adapter = new SqlDataAdapter(sourceCommand)) 
{ 
    adapter.Fill(table); 
} 

table.Columns 
    .OfType<DataColumn>() 
    .ForEach(c => bulk.ColumnMappings.Add(
     new SqlBulkCopyColumnMapping(c.ColumnName, c.ColumnName))); 

bulk.WriteToServer(table) 

,並得到:

給出的ColumnMapping不與源或目標的任何列匹配。

回答

15
DataTable table = new DataTable(); 
using (var adapter = new SqlDataAdapter(sourceCommand)) 
{ 
    adapter.Fill(table); 
} 

using (SqlBulkCopy bulk = new SqlBulkCopy(targetConnection, SqlBulkCopyOptions.KeepIdentity, null) { DestinationTableName = tableName }) 
{ 
    foreach (string columnName in GetMapping(stringSource, stringTarget, tableName)) 
    { 
     bulk.ColumnMappings.Add(new SqlBulkCopyColumnMapping(columnName, columnName)); 
    } 

    targetConnection.Open(); 
    bulk.WriteToServer(table); 
} 

private static IEnumerable<string> GetMapping(string stringSource, string stringTarget, string tableName) 
{ 
    return Enumerable.Intersect(
     GetSchema(stringSource, tableName), 
     GetSchema(stringTarget, tableName), 
     StringComparer.Ordinal); // or StringComparer.OrdinalIgnoreCase 
} 

private static IEnumerable<string> GetSchema(string connectionString, string tableName) 
{ 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    using (SqlCommand command = connection.CreateCommand()) 
    { 
     command.CommandText = "sp_Columns"; 
     command.CommandType = CommandType.StoredProcedure; 

     command.Parameters.Add("@table_name", SqlDbType.NVarChar, 384).Value = tableName; 

     connection.Open(); 
     using (var reader = command.ExecuteReader()) 
     { 
      while (reader.Read()) 
      { 
       yield return (string)reader["column_name"]; 
      } 
     } 
    } 
} 
+0

@abtishchev - 酷和可重複使用。我相信'stringTarget'和'stringSource'是列名稱,對吧? – xameeramir 2014-07-08 11:15:45

+1

@student:hey,IIRC這些是分別連接到源數據庫和目標數據庫的字符串。 – abatishchev 2014-07-08 20:55:22

+0

太棒了。我需要添加一些代碼來正確處理模式和數據庫名稱,但這正是我所需要的。 – 2016-08-16 13:52:07

1

試試這個:SqlBulkCopyColumnMapping Class

希望你正在尋找相同

+0

是的,我這個類正在討論這個類,但是如何在源文件中跳過一列?'.Add(new SqlDataMapping(「deleted-column-on-target」,「」)'?當然,我可以從底層查詢中的源代碼中刪除它 - 「SELECT a,b,c'而不是'SELECT *' - 但這不是一個解決方案 – abatishchev 2010-09-24 07:22:12

+1

如果您不想將其從源複製到目標,只是把它放在映射之外。該映射只會複製指定列中的數據。 – cjk 2010-09-24 08:48:30

9

當使用SqlBulkCopyColumnMapping,只爲它創建的映射將被複制列。

如果您不爲列創建映射,則複製過程將忽略它。

您可以在演示代碼here中看到 - AdventureWorks演示數據庫中的示例源表包含的列數多於映射或複製的列數。

編輯

這是很難確定沒有有關數據庫架構的詳細信息,但在猜測的問題是這樣的說法:

new SqlBulkCopyColumnMapping(c.ColumnName, c.ColumnName) 

從你的描述,這聽起來像不源表中的所有列都存在於目標表中。您需要在SqlBulkCopyColumnMapping構建循環中使用過濾器來跳過目標中不存在的任何列。

我的C#是不夠好,給我有信心將工作一個例子,但在僞它會

foreach column c in sourcetable 
{ 
    if c.ColumnName exists in destination_table.columns 
    { 
      new SqlBulkCopyColumnMapping(c.ColumnName, c.ColumnName) 
    } 
} 

(我敢肯定,這可以將其轉換爲一個lambda表達式)

請注意,在列名匹配但數據類型不兼容的情況下,這不是特別穩健。

+0

查看我更新的帖子。我究竟做錯了什麼?大概我明白 - 來源有一個專欄,但目標 - 不。我應該比較源/目標架構並僅使用列中存在的兩個 – abatishchev 2010-09-24 08:25:41

+0

@abatishchev - 添加更多詳細信息 – 2010-09-24 08:44:29

+0

謝謝!你清除了我的視野。但是你的例子不適合我,因爲我沒有目標表,只有它的名字。所以必須調用'sp_Columns'來確定表列。 – abatishchev 2010-09-24 09:13:45

2

埃德哈珀,這是它看起來就像沒有僞代碼 (在這種情況下,從數據表DT(完全定義)到現有的表在db:

using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString)) 
{ 
    bulkCopy.DestinationTableName = "dbo.DepartmentsItems"; 

    // Write from the source to the destination. 
    foreach (DataColumn c in dt.Columns) 
    { 
     bulkCopy.ColumnMappings.Add(c.ColumnName, c.ColumnName); 
    } 

    bulkCopy.WriteToServer(dt); 
    return dt.Rows.Count; 
}