Как мне исправить этот триггер? - PullRequest
2 голосов
/ 07 апреля 2019

Я реализовал триггер для таблицы, который гарантирует, что таблица не содержит более одной строки со значением 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 - это таблица с тремя столбцами:

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

и следующими строками:

enter image description here

При выполнении запроса:

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

Я получаю сообщение об ошибке:

Msg 50000, Уровень 16, Состояние 1, Процедура FLYINGDUTCHMAN_NEEDS_A_CAPTAIN, Строка xx FLYING DUTCHMAN ИЗБРАНА И НУЖЕН КАПИТАН.

Я ожидаю, что этот запрос должен работать нормально, а триггер не должен запускаться.

Ответы [ 3 ]

2 голосов
/ 07 апреля 2019

Может быть упрощено до:

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
2 голосов
/ 07 апреля 2019

ваш триггер всегда срабатывает, потому что ваше условие if(@captainsum <> 1) всегда истинно, когда ваша сводка в подзапросе равна 1, @captainsum = 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
2 голосов
/ 07 апреля 2019

Поскольку это триггер after, все изменения уже находятся в таблице - вам не нужно запрашивать таблицы inserted и deleted - вот почему вы получаете неправильный результат.

Вы можете просто сделать это:

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