0

我有一个Reservations包含以下列的表格

  1. Reservation_ID
  2. Res_TotalAmount - money
  3. Res_StartDate - datetime
  4. IsDeleted - bit具有默认值的列 - false

因此,当用户尝试删除他的预订时,我创建了一个触发器,而不是删除 - 他只是将列的值更新IsDelete为 true;

到目前为止一切顺利 - 但这位游客可能欠公司一些补偿,例如,当他在预订后 30 天到 20 天取消start_date预订时 - 他欠 30% 的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列的存储过程

Create proc sp_NewReservationTotalAmount(@newTotalAmount money, @resID)
As 
   declare @resID int 

   Update RESERVATIONS set Res_TotalAmount=@newTotalAmount where Reservation_ID=resID

  1. 所以我的第一个问题是它在大小写附近给了我不正确的语法

  2. 我的第二个 - 我会很感激有关如何使触发器和存储过程更好的建议。

4

3 回答 3

3

您的基本缺陷是您似乎希望触发器每行触发一次- SQL Server并非如此。相反,触发器会在每个语句中触发一次,并且伪表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

另外:CASET-SQL 中的语句只是为了返回一个原子值——它不像其他语言中的流控制语句,它不能用于执行代码。因此,您CASE在触发器中的语句完全“丢失”了-它需要在分配或类似的东西中使用....

于 2013-09-30T14:25:53.243 回答
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)如果删除语句影响多行,则此触发器将出现问题。您的语句如“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
于 2013-09-30T14:31:02.210 回答
0

关于case

语法错误。即使它有效(见下文),你也会错过WHENs:

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. 例如,它不像switchC++/C# 中的语句。您只能在查询中使用它,例如

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如前所述。

于 2013-09-30T14:30:01.360 回答