我该如何纠正这个触发器?

How do i correct this trigger?

我在 table 上实现了一个触发器,它确保 table 不包含超过一行,其中一列的位值为 1。但是触发器不知何故无法正常工作。

create trigger [dbo].[flyingdutchman_needs_a_captain]
on [dbo].[flyingdutchman]
after insert, update, delete as
begin
set nocount on;
set rowcount 0;
begin try
declare @captainsum int
set @captainsum = 0     
set @captainsum = (select sum(case when uniontable.iscaptain = 1 then 1 else 0 end) 
from (
    select iscaptain from dbo.flyingdutchman 
    union all
    select inserted.iscaptain as iscaptain from inserted
    union all
    select deleted.iscaptain as iscaptain from deleted
    ) as uniontable
having sum(case when uniontable.iscaptain = 1 then 1 else 0 end)  <> 1)     
if(@captainsum <> 1)
begin
    throw 50000,'flying dutchman is cursed and needs a captain.',1;
end     
end try
begin catch
if xact_state()<>0
    rollback transaction;
throw;
end catch
end

flyingdutchman 是一个 table 三列:

SailorId(int),SailorName(varchar),IsCaptain(bit)

和以下行:

执行查询时:

insert into dbo.flyingdutchman(Sailorname,IsCaptain)
values('Davy Jones',1)

我收到错误:

Msg 50000, Level 16, State 1, Procedure FLYINGDUTCHMAN_NEEDS_A_CAPTAIN, Line xx FLYING DUTCHMAN IS CURSED AND NEEDS A CAPTAIN.

我希望这个查询 运行 正常并且触发器不应该被触发。

因为这是一个 after 触发器,所以所有更改都已经在 table 中 - 您不需要查询 inserteddeleted tables - 这就是你得到错误结果的原因。

您可以简单地这样做:

if 1 <> (select count(*) from dbo.flyingdutchman where iscaptain = 1)
    throw 50000,'flying dutchman is cursed and needs a captain.',1;

但是,我怀疑您应该改为这样做:

declare @captainsCount int;
select @captainsCount = count(*) from dbo.flyingdutchman where iscaptain = 1
if @captainsCount = 0 
    throw 50000,'flying dutchman is cursed and needs a captain.',1;
if @captainsCount > 1 
    throw 50000,'flying dutchman has too many captains.',1;

可以简化为:

create trigger [dbo].[flyingdutchman_needs_a_captain]
on [dbo].[flyingdutchman]
after insert, update, delete as
begin
set nocount on;

begin try   

-- prevents multiple captains
IF  (SELECT COUNT(*) FROM [dbo].[flyingdutchman] WHERE iscaptain = 1   ) > 1 
    throw 50000,'Too many captains is not good.',1;

-- prevents setting all captains off, if one was already assigned
IF NOT EXISTS(SELECT * FROM [dbo].[flyingdutchman] WHERE iscaptain = 1   )
   AND EXISTS(SELECT * FROM deleted WHERE iscaptain = 1   ) 
    throw 50001,'flying dutchman is cursed and needs a captain.',1;


end try
begin catch
if xact_state()<>0
    rollback transaction;
throw;
end catch

end

你的触发器总是触发,因为你的条件 if(@captainsum <> 1) 总是正确的 当您在子查询中的摘要等于 1 时,@captainsum = null 因为您的 having 语句,那么您的变量为 null 或除 1

以外的其他值
create trigger [dbo].[flyingdutchman_needs_a_captain]
on [dbo].[flyingdutchman]
after insert, update, delete as
begin
set nocount on;
set rowcount 0;
begin try
declare @captainsum int
set @captainsum = 0     
set @captainsum = (select sum(case when uniontable.iscaptain = 1 then 1 else 0 end) 
from (
    select iscaptain from dbo.flyingdutchman where SailorId not in (select SailorId from inserted)
    union all
    select inserted.iscaptain as iscaptain from inserted
    union all
    select deleted.iscaptain as iscaptain from deleted
    ) as uniontable
having sum(case when uniontable.iscaptain = 1 then 1 else 0 end)  <> 1)     
if(@captainsum <> 1)
begin
    throw 50000,'flying dutchman is cursed and needs a captain.',1;
end     
end try
begin catch
if xact_state()<>0
    rollback transaction;
throw;
end catch
end