【问题标题】:Skip some columns in SqlBulkCopy跳过 SqlBulkCopy 中的一些列
【发布时间】:2010-09-24 07:13:00
【问题描述】:

我正在使用SqlBulkCopy 对两个具有不同列集的SQL Server 2008(将一些数据从prod 服务器移动到dev)。所以想跳过一些尚不存在/尚未删除的列。

我该怎么做? 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 与源或目标中的任何列都不匹配。

【问题讨论】:

    标签: c# sql-server sql-server-2008 sqlbulkcopy columnmappings


    【解决方案1】:
    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"];
                }
            }
        }
    }
    

    【讨论】:

    • @abtishchev - 很酷且可重复使用。我相信 'stringTarget' 和 'stringSource' 是列名,对吧?
    • @student:嘿,IIRC 这些分别是源数据库和目标数据库的连接字符串。
    • 太棒了。我需要添加一些代码来正确处理模式和数据库名称,但这正是我所需要的。
    • @Kev:坦率地说,我会建议两件事:要么改变你的 IT 部门,要么如果你不能 - 改变你的工作。我们在工作中度过了一段美好的时光,而生命太短了,不能与不专业/过时/愚蠢的同事一起度过。
    • 很高兴使用 sp_columns 而不是 select * from table where 1=0 并从 DataReader 获取 SchemaTable()。你是英雄。
    【解决方案2】:

    当使用 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 表达式)

    请注意,这在列名匹配但数据类型不兼容的情况下并不是特别可靠。

    【讨论】:

    • 查看我更新的帖子。我究竟做错了什么?可能我理解 - 来源有一个专栏,但目标 - 没有。我应该比较源/目标模式并只使用两者中都存在的列
    • @abatishchev - 添加了更多细节
    • 谢谢!你清除了我的视野。但是不幸的是,您的示例不适合我,因为我没有目标表,只有它的名称。所以必须调用sp_Columns 来确定表列。
    【解决方案3】:

    Ed Harper,这就是没有伪代码的样子 (在这种情况下,从 DataTable dt(完全定义)到数据库中的现有表:

    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;
    }
    

    【讨论】:

      【解决方案4】:

      试试这个:SqlBulkCopyColumnMapping Class

      希望您正在寻找相同的

      【讨论】:

      • 是的,我说的正是这个类。但是如何跳过源中的列? .Add(new SqlDataMapping("deleted-column-on-target", "")?当然,我可以在基础查询中从源中删除它 - SELECT a,b,c 而不是 SELECT * - 但这不是解决方案
      • 如果您不想将其从源复制到目标,只需将其排除在映射之外。映射只会复制指定列中的数据。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-18
      • 2013-04-04
      • 1970-01-01
      • 2016-01-10
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      相关资源
      最近更新 更多