SQL Server:更新触发器似乎会影响错误的列

时间:2018-09-29 17:57:19

标签: sql-server triggers sql-insert

感谢您的光临。我正在尝试编写一个SQL Server触发器,当添加包含日期信息的新记录时,会将星期几添加到DayOfWeek列中。这是我的表,按顺序排列各列:

食物表:

FoodName **varchar(20)**  
CategoryID (FK) **int**  
Price **smallmoney**  
StoreID (FK) **int**  
Date **datetime**  
DayOfWeek **varchar(9)**  
ShopperID (FK) **int**  
Week **int**  

这是我写的触发器:

-- Create a trigger to update day of the week when a record is inserted  
CREATE TRIGGER DOW  
ON Food  
FOR INSERT  
AS  
BEGIN  
    -- Declare a variable to hold the date ID  
    DECLARE @dateID DATETIME

    -- Get the date from the new record and store it in @dateID  
    SELECT @dateID = Date FROM Food  

    -- Insert day of the week based on the inserted date  
    INSERT INTO Food (DayOfWeek)  
        SELECT DATENAME(dw, @dateID)  
END  
GO  

SQL Server似乎接受该过程,但是当我运行另一个过程以插入新记录时,出现此错误:

  

信息515,级别16,状态2,过程DOW,第8行[批处理开始第21行]
  无法将值NULL插入表*******的“周”列中;列不允许为空。 INSERT失败。

我不确定为什么此触发器完全影响“周”列。代码应采用为Date输入的值,并使用DATENAME(dw,...)函数返回星期几,该日期应进入DayOfWeek列。我编写了一个存储过程,该存储过程接受日期作为输入并将相应的星期几插入记录中,并且工作正常,但是此触发器似乎不希望合作。我很困惑!

2 个答案:

答案 0 :(得分:0)

触发器的作用:

  • 它从您的表(返回的最后一个)中获取一个Date,不一定是最后插入的值。
  • 它尝试插入仅指定了DayOfWeek中的Date的新记录。
  • 失败,因为至少还必须指定Week

我猜您想改为为插入的行更新DayOfWeek的值。为此,必须有一种方法可以通过了解插入的行的值来标识“食物”表中需要更新的行。为了确保更新正确的行,应该有一个主键可以让您识别它们。当然,您具有这样的主键,而且我猜它的名称为FoodID,所以您可能想这样做:

CREATE TRIGGER DOW ON Food
FOR INSERT
AS
BEGIN
    SET NOCOUNT ON;
    -- update the day of the week for the inserted rows
    UPDATE Food
        SET [DayOfWeek] = DATENAME(dw, f.[Date])
    FROM Food f
        INNER JOIN inserted i ON f.FoodID = i.FoodID
END
GO

答案 1 :(得分:0)

触发器存在一些重大问题。在触发器中,有一个插入的表(在插入和更新时)和已删除的表(在删除和更新时)。您应该使用此表的信息来了解需要更新哪些记录。

This is bad because a trigger can have multiple rows
This SQL simply will not work correctly if you insert multiple rows.
DECLARE @dateID DATETIME
SELECT @dateID = Date FROM Food  

This SQL is trying to insert a new row which is causing your NULL error
It is not trying to update the row you are inserting
INSERT INTO Food (DayOfWeek)  
    SELECT DATENAME(dw, @dateID)  

它必须是INSTEAD OF触发器,以避免对列的空约束。沃尔夫冈的答案仍将导致空约束错误,因为在触发器运行之后插入数据之后,就会发生空约束错误。 INSTEAD OF触发器将代替实际的插入物运行。

CREATE TRIGGER DOW ON Food
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;
    -- update the day of the week for the inserted rows

    INSERT INTO Food (FoodName,CategoryID,Price,StoreID,[Date],ShopperID,[Week],[DayOfWeek])
        SELECT
            FoodName,CategoryID,Price,StoreID,[Date],ShopperID,[Week],DATENAME(dw, [Date]) AS [DayOfWeek]
        FROM inserted
END
GO

就个人而言,我认为存储星期和星期几是个坏主意。您已经有一个可以派生该信息的值(日期)。每当您有多个本质上是重复数据的列时,您都会遇到维护难题。