【问题标题】:SqlBulkInsert with a DataTable to a Linked Server带有 DataTable 的 SqlBulkInsert 到链接服务器
【发布时间】:2013-01-25 19:42:31
【问题描述】:

我在不同的机器上使用 2 个 SQL 2008 服务器。服务器名称为source.ex.comdestination.ex.com

destination.ex.com 链接到source.ex.com,并且具有适当的权限,source.ex.com 可以在destination.ex.com 上写入名为bacon-wrench 的数据库

我已经通过短信登录source.ex.com 并测试了这个查询(成功):

INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
(PunchID, BaconID) VALUES (4,6);

在 C# .NET 4.0 网页中,我连接到 source.ex.com 并执行类似的查询(成功):

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString))
{
    c.Open();
    String sql = @"
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
        (PunchID, BaconID) VALUES (34,56);";
    using(SqlCommand cmd = new SqlCommand(sql, c))
    {
        cmd.ExecuteNonQuery();
    }
}

对于少量的插入语句(比如 20 条或更少),执行这样的操作效果很好:

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString))
{
    c.Open();
    String sql = @"
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
         (PunchID, BaconID) VALUES (34,56);
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
         (PunchID, BaconID) VALUES (22,11);
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
         (PunchID, BaconID) VALUES (33,55);
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
         (PunchID, BaconID) VALUES (1,2);";
    using(SqlCommand cmd = new SqlCommand(sql, c))
    {
        cmd.ExecuteNonQuery();
    }
}

我正在尝试用大约 20000 条记录来做这样的事情。上述方法需要 11 分钟才能完成——我认为这是服务器对我发出尖叫以使其成为某种批量操作。在其他 StackOverflow 线程中,推荐使用 SqlBulkCopy 类,并将其作为参数 DataTable,完美!

所以我构建了一个 DataTable 并尝试将其写入服务器(失败):

DataTable dt = new DataTable();
dt.Columns.Add("PunchID", typeof(int));
dt.Columns.Add("BaconID", typeof(int));
for(int i = 0; i < 20000; i++)
{
    //I realize this would make 20000 duplicate 
    //rows but its not important 
    dt.Rows.Add(new object[] {
        11, 33 
    });
}

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString))
{
    c.Open();
    using(SqlBulkCopy bulk = new SqlBulkCopy(c))
    {
        bulk.DestinationTableName = "[destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]";
        bulk.ColumnMappings.Add("PunchID", "PunchID");
        bulk.ColumnMappings.Add("BaconID", "BaconID");
        bulk.WriteToServer(dt);
    }
}

EDIT2:以下消息是我正在尝试修复的:

网页在bulk.WriteToServer(dt); 崩溃并显示错误消息Database bacon-wrench does not exist please ensure it is typed correctly. 我做错了什么?如何更改它以使其正常工作?

编辑1:

我能够使用以下语法显着加快查询速度。但是对于这么小的记录集来说还是很慢的。

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString))
{
    c.Open();
    String sql = @"
        INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]
         (PunchID, BaconID) VALUES 
         (34,56),
         (22,11),
         (33,55),
         (1,2);";
    using(SqlCommand cmd = new SqlCommand(sql, c))
    {
        cmd.ExecuteNonQuery();
    }
}

【问题讨论】:

    标签: c# sql sql-server-2008 bulkinsert linked-server


    【解决方案1】:

    如果您使用的是 SQL Server 2008+,则可以引入 Table 用户数据类型。准备类型、接收表和存储过程,如下所示。数据类型和存储过程在本地系统上。我一般在代码中有一个if语句检测表是远程还是本地,远程我这样做,本地我用SqlBulkCopy。

    if(TYPE_ID(N'[Owner].[TempTableType]') is null)
    begin
    CREATE TYPE [Owner].[TempTableType] AS TABLE ( [PendingID] uniqueidentifier, [Reject] bit)
    end
    IF  NOT EXISTS (SELECT * FROM [LinkedServer].[DatabaseOnLS].sys.tables where name = 'TableToReceive') 
    EXEC('
    CREATE TABLE [DatabaseOnLS].[Owner].[TableToReceive] ( [PendingID] uniqueidentifier, [Reject] bit)
    ') AT [LinkedServer]
    else
    EXEC('
    TRUNCATE TABLE [DatabaseOnLS].[Owner].[TableToReceive]
    ') AT [LinkedServer]
    
    CREATE PROCEDURE [Owner].[TempInsertTable]
    @newTableType TempTableType readonly
    AS
    BEGIN
    insert into [LinkedServer].[DatabaseOnLS].[Owner].[TableToReceive] select * from @newTableType 
    END
    

    在 C# 代码中,您可以执行以下操作将 DataTable 插入链接服务器上的表中(我使用的是现有的 UnitOfWork,它已经具有连接和事务):

    using (var command = new SqlCommand("TempInsertTable",
                        oUoW.Database.Connection as SqlConnection) { CommandType = CommandType.StoredProcedure }
                    )
    {
        command.Transaction = oUoW.Database.CurrentTransaction as SqlTransaction;
        command.Parameters.Add(new SqlParameter("@newTableType", oTempTable));
        drResults = command.ExecuteReader();
        drResults.Close();
    }
    

    【讨论】:

      【解决方案2】:

      在尝试了包括链接服务器设置、排序规则、同义词等在内的许多事情后,我最终得到了这个错误消息:

      Inserting into remote tables or views is not allowed by using the BCP utility or by using BULK INSERT.

      也许您可以批量插入到本地服务器上的临时表(您的代码可以正常工作),然后从该临时表插入到链接服务器,然后在本地删除临时表。您必须测试性能。

      【讨论】:

      • 哦不!我会按照你的建议尝试。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      • 2016-10-06
      • 2014-02-01
      • 2012-07-21
      • 1970-01-01
      相关资源
      最近更新 更多