【问题标题】:SQL bulk copy : Trigger not getting firedSQL批量复制:触发器没有被触发
【发布时间】:2018-11-10 01:50:06
【问题描述】:

我正在使用 sql 批量复制将数据从 Table1 复制到 Table2 表。我已在 Table2 上应用了触发器,但我的触发器并未在每一行上触发。这是我的触发器和 sqlbulkcopy 函数。

SqlConnection dstConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Destination"].ConnectionString);
string destination = dstConn.ConnectionString;

//Get data from Source in our case T1 
DataTable dataTable = new Utility().GetTableData("Select * From  [db_sfp_ems].[dbo].[tbl_current_data_new] where [start_date]>'" + calculate_daily_Time + "' and status=0" , source);
SqlBulkCopy bulkCopy = new SqlBulkCopy(source, SqlBulkCopyOptions.FireTriggers)
{
    //Add table name of source
    DestinationTableName = "tbl_current_data",
    BatchSize = 100000,
    BulkCopyTimeout = 360
};
bulkCopy.WriteToServer(dataTable);
//MessageBox.Show("Data Transfer Succesfull.");
dstConn.Close();

-----触发器-----

ALTER TRIGGER [dbo].[trgAfterInsert] ON [dbo].[tbl_current_data]
   AFTER INSERT 
AS 
BEGIN

    declare @intime datetime
    declare @sdp_id numeric
    declare @value numeric(9,2)


    SELECT @intime= DATEADD(SECOND, -DATEPART(SECOND, start_date), start_date)  FROM INSERTED

    SELECT @sdp_id= sdp_id  FROM INSERTED
    SELECT @value= value  FROM INSERTED

    INSERT INTO Table3(sdp_id,value,start_date)
    VALUES
    (
           @sdp_id,@value,@intime
    )

【问题讨论】:

  • 我原以为批量复制的重点是尽可能快地完成,而不是触发触发器!
  • 不太确定,但由于大容量复制不会在每一行之后提交,因此您的触发器不会在每一行之后被激活。但就在批量复制提交更改时。
  • 触发器不适用于批量操作,但适用于原子操作。如果你想让触发器工作,你应该逐行处理插入
  • 当我将 BatchSize = 100000 减少到 20 时,我的触发器就会被触发。但我不想要批量大小 20,我想要最小批量大小 100。

标签: sql .net sql-server database-connection


【解决方案1】:

在插入后触发触发器,无论该插入涉及 0、1 还是多条记录,对触发器都没有影响。因此,即使您要插入一大堆记录,触发器也只会触发一次。这是设计使然,并非特定于BULK_INSERT;这适用于每种插入。这也意味着inserted 伪表可以保存0、1 或多条记录。这是一个常见的陷阱。确保以可以处理多条记录的方式编写触发器。例如:如果inserted 拥有多条记录,SELECT @sdp_id= sdp_id FROM INSERTED 将无法按预期工作。该变量将被设置,但您无法知道它将保存什么值(来自哪个插入的记录)。

这都是基于集合的 SQL 哲学的一部分,最好不要尝试通过使用循环或其他 RBAR 方法来破坏该哲学。保持固定的心态。

【讨论】:

  • 所以你的意思是,使用sqlbulkcopy时不可能生成触发器
  • 不,可以通过批量插入触发触发器,这就是你有SqlBulkCopyOptions.FireTriggers 的原因。 不可能为批量插入中的每一行触发触发器。要么全有,要么全无。如果真的,真的 需要,您可以遍历datatable 中的行并触发单独的插入。不过,我很难想到需要这样做的情况。
【解决方案2】:

你的触发器坏了。在 SQL Server 中,触发器一次处理多行。假设inserted 有一行是致命错误——我希望它会导致语法错误。

我认为这是您想要的代码:

ALTER TRIGGER [dbo].[trgAfterInsert] ON [dbo].[tbl_current_data]
   AFTER INSERT 
AS 
BEGIN
    INSERT INTO Table3 (sdp_id, value, start_date)
        SELECT sdp_id, value, 
               DATEADD(SECOND, -DATEPART(SECOND, start_date), start_date)
        FROM inserted i;
END;

除了正确之外,另一个优点是代码更简单。

注意:您将“秒”部分设置为 0。但是 -- 根据类型 -- start_date 可能会保留小数秒。如果这是一个问题,请提出另一个问题。

【讨论】:

  • 我必须检查@intime 参数,这就是为什么我没有按照你的建议编写触发器。我必须检查以下条件。 if (((SELECT datediff(minute, (select top 1 [start_date] FROM [tbl_processdata_history] ​​where sdp_id=@sdp_id order by currentdata_id desc), @intime)) >= 5 )) end
  • 之后我必须将记录插入到表中。
  • @AkshayDod 。 . .您的触发器没有检查任何此类条件。如果您还有其他问题,请作为another 问题提出。您的触发器只是损坏了,因为它假定 inserted 只有一行。这是一个错误的假设。
猜你喜欢
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多