我有一个Reservations
表,其中包含以下列
Reservation_ID
Res_TotalAmount - money
Res_StartDate - datetime
IsDeleted - bit
列,默认值为false 因此,当用户尝试删除他的预订时,我创建了一个触发器,而不是删除 - 他只是将列IsDelete
的值更新为true;
到目前为止一直很好 - 但是这位游客可能会欠公司一些补偿,例如当他从预订的start_date
起30天到20天取消他的预订时 - 他欠了{%1}的{ {1}}等等
这是我的触发器
Res_TotalAmount
由于我必须使用触发器和存储过程,您会看到我在触发器的末尾调用一个只更新Create Trigger tr_TotalAMountUpdateAfterInsert on RESERVATIONS after Delete
As
Begin
Declare @period int
Declare @oldResAmount int
Declare @newAmount money
Declare @resID int
Select @resID = Reservation_ID from deleted
select @oldResAmount = Res_TotalAmount from deleted
Select @period= datediff (day,Res_StartDate,GETDATE()) from deleted
case
@period is between 30 and 20 then @newAmount=30%*@oldResAmount
@period is between 20 and 10 then @newAmount=50%*@oldResAmount
end
exec sp_NewReservationTotalAmount @newAmount @resID
End
GO
列的存储过程
Res_TotalAmount
GO
所以我的第一个问题是它在case
我的第二个 - 我很感激建议如何使触发器和存储过程更好。
答案 0 :(得分:3)
您的根本缺陷是,您似乎希望触发器每行一次 - 这在SQL Server中是 NOT 。相反,触发器会在每个语句时触发,而伪表Deleted
可能包含多行。
鉴于该表可能包含多行 - 您希望在这里选择哪一行?
Select @resID = Reservation_ID from deleted
select @oldResAmount = Res_TotalAmount from deleted
Select @period= datediff (day,Res_StartDate,GETDATE()) from deleted
未定义 - 您可以从Deleted
中的任意行获取值。
您需要使用Deleted
WILL 包含多行的知识重写整个触发器!您需要使用基于集合的操作 - 不要只期望Deleted
中的一行!
另外:T-SQL中的CASE
语句只是为了返回一个原子值 - 它是不一个流控制语句,就像在其他语言中一样,它不能用于执行码。因此,触发器中的CASE
语句完全“丢失” - 它需要在作业中使用或类似......
答案 1 :(得分:1)
1)这是CASE语句的正确语法。请注意:
我已经包含了一个“ELSE”案例,因此当@period不在给定范围内时,你不会得到一个未定义的值
SELECT @newAmount =
CASE
WHEN @period between 10 and 20 then 0.5 * @oldResAmount
WHEN @period between 20 and 30 THEN 0.3 * @oldResAmount
ELSE @oldResAmount
END
2)如果delete语句影响多行,您将遇到此触发器的问题。您的语句如“SELECT @resID = Reservation_ID from deleted;”将只是随机地从已删除的表中分配一个值。
修改
以下是一个基于集合的问题解决方案的示例,当在事务中“删除”多行时,该方法仍然有效(仅限示例代码;未经过测试):
Create Trigger tr_TotalAMountUpdateAfterInsert on RESERVATIONS after Delete
As
Begin
UPDATE RESERVATIONS
SET Res_TotalAmount =
d.Res_TotalAmount * dbo.ufn_GetCancellationFactor(d.Res_StartDate)
FROM RESERVATIONS r
INNER JOIN deleted d ON r.Reservation_ID = d.Reservation_ID
End
GO
CREATE FUNCTION dbo.ufn_GetCancellationFactor (@scheduledDate DATETIME)
RETURNS FLOAT AS
BEGIN
DECLARE @cancellationFactor FLOAT;
DECLARE @period INT = DATEDIFF (DAY, @scheduledDate, GETDATE());
SELECT @cancellationFactor =
CASE
WHEN @period <= 10 THEN 1.0 -- they owe the full amount (100%)
WHEN @period BETWEEN 11 AND 20 THEN 0.5 -- they owe 50%
WHEN @period BETWEEN 21 AND 30 THEN 0.3 -- they owe 30%
ELSE 0 -- they owe nothing
END
RETURN @cancellationFactor;
END;
GO
答案 2 :(得分:0)
关于case
:
语法错误。即使它有效(见下文),你也会错过WHEN
s:
case
WHEN @period is between 30 and 20 then @newAmount=30%*@oldResAmount
WHEN @period is between 20 and 10 then @newAmount=50%*@oldResAmount
end
然而,case
语句不能以这种方式使用。在您需要的上下文中,您需要使用if
。例如,它不像C ++ / C#中的switch
语句。您只能在
SELECT
case
WHEN @period is between 30 and 20 then value1
WHEN @period is between 20 and 10 then value2
end
说了上面的话:我实际上并没有阅读你的所有代码。但是现在我已经阅读了其中的一些内容,了解触发器在SQL Server中的工作原理非常重要,正如mark_s
所述。