案例
我有两个包含以下结构的表(我删除了其他34个与问题无关的列)
Table1
------
Id
LastAccessed
Data1
Data2
Table1_History
------
_Id
Action
Id
LastAccessed
Data1
Data2
每次用户阅读记录(使用特定程序)时,LastAccessed
时间戳都会发生变化。
我为Table1
添加了一个触发器,如果Data1
或Data2
(或者我的情况下任何其他列更改),它会将记录复制到历史记录表中;但是,如果LastAccessed
更改,我不想要副本或记录。
这是我的触发器
CREATE TRIGGER [TRIGGER!]
ON TABLE1
AFTER UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
IF NOT UPDATE([LastAccessed]) BEGIN
IF EXISTS(SELECT * FROM inserted) BEGIN
INSERT INTO [Table1_Hist](
Action, Id, LastAccessed, Data1, Data2
) SELECT
'EDIT', Id, LastAccessed, Data1, Data2
FROM deleted
END ELSE BEGIN
INSERT INTO [Table1_Hist](
Action, Id, LastAccessed, Data1, Data2
) SELECT
'DELETE', Id, LastAccessed, Data1, Data2
FROM deleted
END
END
END
如果LastAccessed和Data1都发生了变化(我想要),则此触发器不会复制行。要解决此问题,我可以将IF
语句更改为
IF UPDATE(Id) OR UPDATE(Data1) OR UPDATE(Data2) ... BEGIN
在这种情况下,它将按预期工作
问题:
由于我的表中有34列,因此在IF
语句中指定每一列并不容易。
有没有更简单的方法呢?
答案 0 :(得分:3)
希望有人有一个比这更优雅的答案,但最糟糕的情况是你可以通过一个简单的查询为所有34列生成丑陋的IF语句:
SELECT CASE WHEN column_id = 1 THEN 'IF ' ELSE 'OR ' END + 'UPDATE(' + name + ')'
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[dbo].[Table1]')
AND name <> 'LastUpdated'
ORDER BY column_id
然后你可以为IF语句剪切并粘贴结果......不是一个优雅的解决方案,但它比手动输入更快。
答案 1 :(得分:0)
CREATE TRIGGER [TRIGGER!]
ON TABLE1
AFTER UPDATE
AS
BEGIN
INSERT INTO Table1_Hist (
Action
Id
LastAccessed
Data1
Data2
)
SELECT 'UPDATE',d.ID,d.LastAccessed,d.Data1,d.Data2, etc., etc.
FROM DELETED d
INNER JOIN INSERTED i ON d.id = i.id
WHERE
COALESCE(d.data1,'') <> COALESCE(i.data1,'') OR
COALESCE(d.data2,'') <> COALESCE(i.data2,'') OR
<repeat for every attribute EXCEPT LastAccessed>
END
这也适用于多行的插入或更新,而您的方法可能会插入数据实际上没有更改的行。
此外,您希望具有单独的UPDATE和DELETE触发器,因为在实际DELETE的情况下,您的查询应该只从DELETED转储完整的行集。