1

案子

我有两个具有以下结构的表(我删除了与问题无关的 34 个其他列)

Table1
------
Id
LastAccessed
Data1
Data2

Table1_History
------
_Id
Action
Id
LastAccessed
Data1
Data2

每次用户读取记录(使用特定程序)时,LastAccessed时间戳都会改变。

我附加了一个触发器,如果​​或(或在我的情况下任何其他列更改),Table1则将记录复制到历史表;但是我不想要副本或记录以防万一。Data1Data2LastAccessed

这是我的触发器

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语句中指定每一列并不容易。有没有更简单的方法来做到这一点?

4

2 回答 2

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 语句剪切和粘贴结果......这不是一个优雅的解决方案,但它比手动输入要快。

于 2013-10-08T10:30:56.033 回答
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 转储完整的行集。

于 2013-10-22T01:18:15.090 回答