SQL Server - 插入触发器后 - 更新同一个表中的另一列

时间:2011-03-17 15:53:11

标签: sql-server sql-server-2008 triggers

我有这个数据库触发器:

CREATE TRIGGER setDescToUpper
ON part_numbers
 AFTER INSERT,UPDATE
AS
DECLARE @PnumPkid int, @PDesc nvarchar(128)

SET @PnumPkid = (SELECT pnum_pkid FROM inserted)
SET @PDesc = (SELECT UPPER(part_description) FROM inserted)

UPDATE part_numbers set part_description_upper = @PDesc WHERE pnum_pkid=@PnumPkid

GO

这是个坏主意吗?那就是更新同一个表上的列。我希望它为插入和更新启动。

它有效,我只是害怕周期性的情况。触发器内的更新会一次又一次地触发触发器。 会发生这种情况吗?

请不要挑剔大写的东西。疯狂的情况。

7 个答案:

答案 0 :(得分:39)

它取决于当前在数据库上设置的触发器的递归级别。

如果你这样做:

SP_CONFIGURE 'nested_triggers',0
GO
RECONFIGURE
GO

或者这个:

ALTER DATABASE db_name
SET RECURSIVE_TRIGGERS OFF

上面的那个触发器不会被再次调用,你会安全的(除非你遇到某种僵局;这可能是可能的,但也许我错了)。

不过,我认为这是一个好主意。更好的选择是使用INSTEAD OF trigger。这样您就可以避免通过DB执行第一次(手动)更新。只会执行触发器内定义的那个。

INSTEAD OF INSERT触发器将是这样的:

CREATE TRIGGER setDescToUpper ON part_numbers
INSTEAD OF INSERT
AS
BEGIN
    INSERT INTO part_numbers (
        colA,
        colB,
        part_description
    ) SELECT
        colA,
        colB,
        UPPER(part_description)
    ) FROM
        INSERTED
END
GO

这将自动“替换”原始INSERT语句,并将{UP}调用应用于part_description字段。

INSTEAD OF UPDATE触发器类似(并且我不建议您创建单个触发器,将它们分开)。

此外,这解决了@Martin评论:它适用于多行插入/更新(您的示例没有)。

答案 1 :(得分:15)

另一种选择是将更新语句包含在IF语句中,并调用TRIGGER_NESTLEVEL()以限制更新再次运行。

CREATE TRIGGER Table_A_Update ON Table_A AFTER UPDATE 
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
    UPDATE a
    SET Date_Column = GETDATE()
    FROM Table_A a
    JOIN inserted i ON a.ID = i.ID
END

当触发器最初运行时,TRIGGER_NESTLEVEL设置为1,因此将执行更新语句。该更新语句将依次触发相同的触发器,除非此时TRIGGER_NESTLEVEL设置为2并且不会执行update语句。

您还可以先检查TRIGGER_NESTLEVEL,如果它大于1,则调用RETURN退出触发器。

IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

答案 2 :(得分:6)

改为使用计算列。使用计算列而不是触发器几乎总是更好的主意。

使用UPPER函数查看计算列的下面示例:

create table #temp (test varchar (10), test2 AS upper(test))
insert #temp (test)
values ('test')
select * from #temp

并不是听起来像是破纪录或任何东西,但这非常重要。切勿编写在多个记录插入/更新/删除时无法正常工作的触发器。这是一种非常糟糕的做法,因为迟早会发生其中一种情况,并且您的触发器将导致数据完整性问题,因为它不会精确地失败,它只会在其中一条记录上运行该进程。这可能需要很长时间,直到有人发现混乱,并且通过它们通常无法正确修复数据。

答案 3 :(得分:1)

是的...有一个额外的步骤来更新一个表,您可以在其中设置初始插入中的值可能是一个额外的,可避免的过程。 您是否可以访问原始插入语句,您可以使用UPPER(part_description)值将part_description插入part_description_upper列?

在思考之后,你可能没有可能的访问权限,所以也应该给出一些选择......

1)取决于是否需要这个part_description_upper列,如果只是为了“查看”,那么可以使用返回的part_description值和“ToUpper()”它(取决于编程语言)。

2)如果想避免“实时”处理,可以创建一个sql作业,以便在低流量时段内每天检查一次您的值,并将该列更新为当前未设置的任何UPPER part_description值。 / p>

3)使用你的触发器(并注意其他人提到的递归)......

HTH

戴夫

答案 4 :(得分:1)

是的,它会递归调用你的触发器,除非你关闭递归触发器设置:

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

MSDN对递归触发器标题下http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx处的行为有一个很好的解释。

答案 5 :(得分:1)

当无事可做时退出触发器可能更安全。通过关闭RECURSIVE来检查嵌套级别或更改数据库可能会出现问题。

Ms sql在触发器中提供了一种简单的方法,以查看是否已更新特定列。使用UPDATE()方法查看某些列是否已更新,例如UPDATE(part_description_upper)。

IF UPDATE(part_description_upper)
  return

答案 6 :(得分:-3)

create or replace 
TRIGGER triggername BEFORE INSERT  ON 
table FOR EACH ROW 
BEGIN
/*
Write any select condition if you want to get the data from other tables
*/
:NEW.COLUMNA:= UPPER(COLUMNA); 
--:NEW.COUMNa:= NULL;
END; 

上述触发器将在插入前更新列值。 例如,如果我们将COLUMNA的值赋予null,它将为每个insert语句将列更新为null。