SQL触发器不一致地触发

时间:2018-02-16 10:26:36

标签: sql-server tsql database-trigger

我在一个有效的表上有一个SQL触发器......大多数时候。我无法弄清楚为什么有时字段为NULL

触发器的工作原理是,只要在字段中修改了某些内容,就更新LastUpdateTime,并在首次创建时更新InsertDatetime。

出于某种原因,这似乎只能工作一段时间。

ALTER TRIGGER [dbo].[DateTriggerTheatreListHeaders]
   ON  [dbo].[TheatreListHeaders]
   AFTER INSERT,UPDATE
AS 
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS(SELECT * FROM DELETED)
BEGIN
        UPDATE ES
                SET InsertDatetime = Getdate()
                ,LastUpdateDateTime = Getdate()
                FROM TheatreListHeaders es
                JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER

END 
IF UPDATE(LastUpdateDateTime) OR UPDATE(InsertDatetime)
    RETURN;

IF EXISTS (
    SELECT
        *
    FROM
        INSERTED I
        JOIN
        DELETED D
            -- make sure to compare inserted with (same) deleted person
            ON D.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER

    )
BEGIN
        UPDATE ES
                SET InsertDatetime = ISNULL(es.Insertdatetime,Getdate())
                ,LastUpdateDateTime = Getdate()
                FROM TheatreListHeaders es
                JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER

END
END

2 个答案:

答案 0 :(得分:0)

一种更简单有效的方法来做你想做的事情,就像是......

ALTER TRIGGER [dbo].[DateTriggerTheatreListHeaders]
   ON  [dbo].[TheatreListHeaders]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

--Determine if this is an INSERT OR UPDATE Action .
DECLARE @Action as char(1);
    SET @Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
                         AND EXISTS(SELECT * FROM DELETED)
                        THEN 'U'  -- Set Action to Updated.
                        WHEN EXISTS(SELECT * FROM INSERTED)
                        THEN 'I'  -- Set Action to Insert.   
                    END);

UPDATE ES
    SET  InsertDatetime     = CASE WHEN @Action = 'U' 
                                    THEN ISNULL(es.Insertdatetime,Getdate()) 
                                    ELSE  Getdate()
                              END
        ,LastUpdateDateTime = Getdate()
FROM TheatreListHeaders es
JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER;

END

答案 1 :(得分:0)

"如果更新()"在SQL Server IMO中定义/实现很差。它没有做暗示的事情。该函数仅确定列是否由触发语句中的值设置。对于插入,每个列都隐式(如果没有显式)赋值。因此,它在插入触发器中没有用,并且难以在支持插入和更新的单个触发器中使用。有时最好编写单独的触发器。

您是否了解递归触发器? insert语句将执行更新同一个表的触发器。这会导致触发器再次执行等。(数据库)递归触发器选项是关闭的(这是典型的)还是调整逻辑以支持它?

您对此表的插入/更新/合并语句有什么期望?这可以追溯到您的要求。触发器是否忽略任何直接设置datetime列的尝试并将其设置在触发器始终内?

最后,"究竟有什么作用?#34;实际上意味着您是否有一个可以重现您的问题的测试用例。如果你不这样做,那么你就无法修复"没有特定故障情况的逻辑。但上述评论应该给你足够的线索。说实话,你的逻辑似乎过于复杂。我将补充说,如果在更新期间现有值为null,则将insertdatetime设置为getdate的方式也存在逻辑上的缺陷。 IMO,它应该拒绝任何尝试将值设置为null的更新,因为这会覆盖永远不会更改的事实。 M.Ali提供了一个可用的示例,但包括创建的时间戳问题。下面是一个演示不同路径的示例(假设递归触发选项已关闭)。它不包括拒绝逻辑 - 您应该考虑。请仔细注意合并执行的输出。

use tempdb;
set nocount on;
go

create table zork (id integer identity(1, 1) not null primary key, 
    descr varchar(20) not null default('zippy'), 
    created datetime null, modified datetime null); 
go
create trigger zorktgr on zork for insert, update as 
begin
    declare @rc int = @@rowcount;
    if @rc = 0 return;
    set nocount on;

   if update(created) 
        select 'created column updated', @rc as rc;
   else
        select 'created column NOT updated', @rc as rc;

    if exists (select * from deleted) -- update :: do not rely on @@rowcount
       update zork set modified = getdate()
       where exists (select * from inserted as ins where ins.id = zork.id);
    else
       update zork set created = getdate(), modified = getdate()
       where exists (select * from inserted as ins where ins.id = zork.id);
end;
go

insert zork default values; 
select * from zork;

insert zork (descr) values ('bonk');
select * from zork;

update zork set created = null, descr = 'upd #1' where id = 1;
select * from zork;

update zork set descr = 'upd #2' where id = 1;
select * from zork;
waitfor delay '00:00:02';

merge zork as tgt 
using (select 1 as id, 'zippity' as descr union all select 5, 'who me?') as src 
on tgt.id = src.id 
when matched then update set descr = src.descr
when not matched then insert (descr) values (src.descr)
;
select * from zork;
go
drop table zork;