Instead Of Insert 触发器不适用于我通过 C# 代码插入

Instead Of Insert trigger doesn't work for my insert via C# code

我有以下触发器:

CREATE TRIGGER insert_or_update_AccountNews
ON AccountNews
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE
        @AccountNumber bigint,
        @NewsId int,
        @TariffPlan nvarchar(1024)

    SELECT @AccountNumber = INSERTED.AccountNumber,
           @NewsId = INSERTED.NewsId,
           @TariffPlan = INSERTED.TariffPlan
    FROM INSERTED

    IF EXISTS (SELECT NewsId FROM [AccountNews] 
               WHERE AccountNumber = @AccountNumber AND NewsId = @NewsId)
        UPDATE [AccountNews]
        SET TariffPlan  = @TariffPlan
        WHERE AccountNumber = @AccountNumber
          AND NewsId = @NewsId
    ELSE
        INSERT INTO [AccountNews] (NewsId, AccountNumber, TariffPlan)
            SELECT @NewsId, @AccountNumber, @TariffPlan
END;

我有一个 table,如你所见,它叫做 AccountNews。它有以下列:

AccountNumber, NewsId, TariffPlan

我的想法是当我在 table 中插入一些东西时,触发器将确定数据是否存在(我有 AccountNumberNewsId 的唯一约束)或不存在。如果数据不存在 - 插入,否则 - 更新。

它通过 SQL 控制台完美运行,例如:

insert into  AccountNews (NewsId, AccountId, TariffPlan) 
values (12345, 777777, 'Hello world');

insert into  AccountNews (NewsId, AccountId, TariffPlan) 
values (12345, 777777, 'Hello world 2');

接下来,我有这个 C# 代码来插入数据:

DataTable table = await ReadAsStringAsync(file, newsId);

var connectionString = config.GetConnectionString("MyDbConnection");

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlBulkCopy bcp = new SqlBulkCopy(connection))
{
    connection.Open();

    bcp.BatchSize = 1000;

    bcp.DestinationTableName = "[dbo].[AccountNews]";

    bcp.ColumnMappings.Add("NewsId", "NewsId");
    bcp.ColumnMappings.Add("AccountNumber", "AccountNumber");
    bcp.ColumnMappings.Add("TariffPlan", "TariffPlan");
    bcp.ColumnMappings.Add("Date", "Date");

    await bcp.WriteToServerAsync(table);
}

在这种情况下,我没有看到触发器的结果。当我加载一些已经在我的数据库中的数据时,我有一个唯一约束异常。

要创建仅 UPSERT table,您可以像这样添加触发器:

use tempdb
go

drop table if exists AccountNews 

create table AccountNews
( 
  AccountNumber bigint,
  NewsId int,
  TariffPlan nvarchar(1024),
  constraint pk_AccountNews 
    primary key (AccountNumber, NewsId)
)
go
CREATE TRIGGER insert_or_update_AccountNews
ON AccountNews
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    merge AccountNews as target
    using (select * from inserted) as source
    on (target.AccountNumber = source.AccountNumber and target.NewsId = source.NewsId)
    when matched then
        update set TariffPlan = source.TariffPlan
    when not matched then
        insert (AccountNumber, NewsId, TariffPlan)
        values (source.AccountNumber, source.NewsId, source.TariffPlan);
END;

或没有 MERGE(不允许在一个批次中重复):

CREATE TRIGGER insert_or_update_AccountNews
ON AccountNews
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    with q as
    (
      select a.*, i.TariffPlan NewTariffPlan
      from AccountNews a 
      join inserted i
        on a.AccountNumber = i.AccountNumber
       and a.NewsId = i.NewsId
    )
    update q set TariffPlan = NewTariffPlan;

    insert into AccountNews(AccountNumber,NewsId,TariffPlan)
    select AccountNumber,NewsId,TariffPlan
    from inserted i
    where not exists
    (
       select * 
       from AccountNews a
       where a.AccountNumber = i.AccountNumber
         and a.NewsId = i.NewsId
    );
END;

go

并且您选择使用 SqlBulkCopyOptions 进行触发器和约束检查,从应用程序批量加载时通常应该这样做,因为绕过约束或触发器需要对 table.