我在一个有效的表上有一个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
答案 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;