触发器中的case附近语法不正确

时间:2013-09-30 14:15:56

标签: sql-server tsql triggers

我有一个Reservations表,其中包含以下列

  1. Reservation_ID
  2. Res_TotalAmount - money
  3. Res_StartDate - datetime
  4. IsDeleted - bit列,默认值为false
  5. 因此,当用户尝试删除他的预订时,我创建了一个触发器,而不是删除 - 他只是将列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

    1. 所以我的第一个问题是它在case

    2. 附近给出了错误的语法
    3. 我的第二个 - 我很感激建议如何使触发器和存储过程更好。

3 个答案:

答案 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语句的正确语法。请注意:

  • 我改变了与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所述。