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

发布于 2024-10-24 16:00:33 字数 518 浏览 1 评论 0原文

我有这个数据库触发器:

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

这是一个坏主意吗?那就是更新同一个表上的列。我希望它在插入和更新时都触发。

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

请不要对大写字母吹毛求疵。疯狂的情况。

I've got this database trigger:

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

Is this a bad idea? That is to update a column on the same table. I want it to fire for both insert and update.

It works, I'm just afraid of a cyclical situation. The update, inside the trigger, fires the trigger, and again and again. Will that happen?

Please, don't nitpick at the upper case thing. Crazy situation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

久光 2024-10-31 16:00:33

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

如果你这样做:

SP_CONFIGURE 'nested_triggers',0
GO
RECONFIGURE
GO

或者这样:

ALTER DATABASE db_name
SET RECURSIVE_TRIGGERS OFF

上面的触发器将不会再次被调用,并且你将是安全的(除非你陷入某种死锁;这可能是可能的,但也许我错了)。

尽管如此,我认为这是一个好主意。更好的选择是使用INSTEAD OF 触发器。这样您就可以避免在数据库上执行第一次(手动)更新。只有触发器内定义的才会被执行。

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 语句,并对 part_description 字段应用显式 UPPER 调用。

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

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

It depends on the recursion level for triggers currently set on the DB.

If you do this:

SP_CONFIGURE 'nested_triggers',0
GO
RECONFIGURE
GO

Or this:

ALTER DATABASE db_name
SET RECURSIVE_TRIGGERS OFF

That trigger above won't be called again, and you would be safe (unless you get into some kind of deadlock; that could be possible but maybe I'm wrong).

Still, I do not think this is a good idea. A better option would be using an INSTEAD OF trigger. That way you would avoid executing the first (manual) update over the DB. Only the one defined inside the trigger would be executed.

An INSTEAD OF INSERT trigger would be like this:

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

This would automagically "replace" the original INSERT statement by this one, with an explicit UPPER call applied to the part_description field.

An INSTEAD OF UPDATE trigger would be similar (and I don't advise you to create a single trigger, keep them separated).

Also, this addresses @Martin comment: it works for multirow inserts/updates (your example does not).

弱骨蛰伏 2024-10-31 16:00:33

另一种选择是将更新语句包含在 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 并且更新语句将不会被执行。

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

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

Another option would be to enclose the update statement in an IF statement and call TRIGGER_NESTLEVEL() to restrict the update being run a second time.

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

When the trigger initially runs the TRIGGER_NESTLEVEL is set to 1 so the update statement will be executed. That update statement will in turn fire that same trigger except this time the TRIGGER_NESTLEVEL is set to 2 and the update statement will not be executed.

You could also check the TRIGGER_NESTLEVEL first and if its greater than 1 then call RETURN to exit out of the trigger.

IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
饭团 2024-10-31 16:00:33

请改用计算列。使用计算列几乎总是比触发器更好的主意。

请参阅下面使用 UPPER 函数的计算列的示例:

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

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

Use a computed column instead. It is almost always a better idea to use a computed column than a trigger.

See Example below of a computed column using the UPPER function:

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

And not to sound like a broken record or anything, but this is critically important. Never write a trigger that will not work correctly on multiple record inserts/updates/deletes. This is an extremely poor practice as sooner or later one of these will happen and your trigger will cause data integrity problems asw it won't fail precisely it will only run the process on one of the records. This can go a long time until someone discovers the mess and by themn it is often impossible to correctly fix the data.

神魇的王 2024-10-31 16:00:33

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

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

IF UPDATE(part_description_upper)
  return

It might be safer to exit the trigger when there is nothing to do. Checking the nested level or altering the database by switching off RECURSIVE can be prone to issues.

Ms sql provides a simple way, in a trigger, to see if specific columns have been updated. Use the UPDATE() method to see if certain columns have been updated such as UPDATE(part_description_upper).

IF UPDATE(part_description_upper)
  return
留一抹残留的笑 2024-10-31 16:00:33

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

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

MSDN 在 http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx 位于“递归触发器”标题下。

Yes, it will recursively call your trigger unless you turn the recursive triggers setting off:

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

MSDN has a good explanation of the behavior at http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx under the Recursive Triggers heading.

云巢 2024-10-31 16:00:33

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

经过思考,您可能没有访问权限,因为您可能会这样做,所以也应该提供一些选项...

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

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

3)跟随你的触发器(并像其他人提到的那样观察递归)......

HTH

Dave

Yea...having an additional step to update a table in which you can set the value in the inital insert is probably an extra, avoidable process.
Do you have access to the original insert statement where you can actually just insert the part_description into the part_description_upper column using UPPER(part_description) value?

After thinking, you probably don't have access as you would have probably done that so should also give some options as well...

1) Depends on the need for this part_description_upper column, if just for "viewing" then can just use the returned part_description value and "ToUpper()" it (depending on programming language).

2) If want to avoid "realtime" processing, can just create a sql job to go through your values once a day during low traffic periods and update that column to the UPPER part_description value for any that are currently not set.

3) go with your trigger (and watch for recursion as others have mentioned)...

HTH

Dave

染墨丶若流云 2024-10-31 16:00:33
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,则每个插入语句都会将该列更新为 null。

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; 

The above trigger will update the column value before inserting.
For example if we give the value of COLUMNA as null it will update the column as null for each insert statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文