尝试使用触发器T-SQL插入数据时出错

时间:2012-09-27 18:27:37

标签: sql-server tsql triggers

下面是我们的一个SQL表上用于任何插入/更新操作的触发器。 99/100次此触发器工作正常但是我们偶尔会收到此错误消息:

  

无法将值NULL插入“TransactionDate”列表中   'AgentResourcesU01.dbo.TransactionLog';列不允许空值   INSERT失败。该声明已被终止。

从Insert语句中可以看出,事务日志表中的列是TransactionDate, Operator, TableName, Action, TableStringUserId。我在开始的SELECT语句中设置了变量@transDate,因为在我看来,除非有坏数据进入,否则不应该有NULL进入。

有什么想法吗?

    BEGIN
        SELECT @symetraNumber = SymetraNumber, @lastChangeOperator = LastChangeOperator, @transDate = LastChangeDate, @entityType = EntityType, 
        @firstName = FirstName, @lastName = LastName, @suffix = NameSuffix, @corpName = CorporateName, @controlId = ControlId
        FROM inserted

        IF @firstName IS NULL SET @firstName = 'NULL'
        IF @lastName IS NULL SET @lastName = 'NULL'
        IF @suffix IS NULL SET @suffix = 'NULL'
        IF @corpName IS NULL SET @corpName = 'NULL'
        IF @controlId IS NULL SET @controlId = 'NULL'

        SET @tableString = 'SymNum:' + @symetraNumber + ' EntType:' + @entityType + ' Fname:' + @firstName + ' Lname:' + @lastname + ' Suff:' + @suffix +
            ' CorpName:' + @corpName + ' ctrlId:' + @controlId

        INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action, TableString, UserId)
        VALUES (@transDate, 'Op', @tableName, @action, @tableString, @lastChangeOperator)
    END

2 个答案:

答案 0 :(得分:1)

为了证明Marc的观点,你可以用一套基于集合的方式做到这一点,没有所有这些讨厌的变量和IF检查:

INSERT dbo.TransactionLog
(
  TransactionDate, 
  Operator, 
  TableName, 
  Action,
  TableString,
  UserId
)
SELECT 
  LastChangeDate, 
  'Op',
  @TableName,
  @action, 
     'SymNum:'      + COALESCE(SymetraNumber, 'NULL')
     + ' EntType:'  + COALESCE(EntityType,    'NULL')
     + ' Fname:'    + COALESCE(FirstName,     'NULL')
     + ' Lname:'    + COALESCE(LastName,      'NULL')
     + ' Suff:'     + COALESCE(NameSuffix,    'NULL')
     + ' CorpName:' + COALESCE(CorporateName, 'NULL')
     + ' ctrlId:'   + COALESCE(ControlId,     'NULL'),
  LastChangeOperator
FROM inserted; 

如果基础表中的LastChangeDate为NULL,则将其标记为NOT NULL,修复插入NULL的问题,或两者兼而有之。触发器不应该知道这个约束,但你可以通过做这样的事情来解决它(如果值为NULL,立即将其设置为):

...
  UserId
)
SELECT 
  COALESCE(LastChangeDate, CURRENT_TIMESTAMP), 
  'Op',
...

答案 1 :(得分:0)

[1]我假设您有一个INSERT / UPDATE / DELETE 触发器,当有人试图从基表中删除行时,触发器中的inserted表将为零行。看看这个例子:

CREATE TABLE MyTableWithTrigger (
    MyID INT IDENTITY PRIMARY KEY,
    LastUpdateDate DATETIME NOT NULL
);
GO

CREATE TABLE TransactionLog (
    TransactionLogID INT IDENTITY PRIMARY KEY,
    CreateDate DATETIME NOT NULL DEFAULT GETDATE(),
    LastUpdateDate DATETIME NOT NULL,
    MyID INT NOT NULL
);
GO

CREATE TRIGGER trIUD_MyTableWithTrigger_Audit
ON MyTableWithTrigger
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    DECLARE @LastUpdateDate DATETIME, @MyID INT;
    SELECT @LastUpdateDate=i.LastUpdateDate, @MyID=i.MyID
    FROM inserted i;

    INSERT TransactionLog (LastUpdateDate, MyID)
    VALUES (@LastUpdateDate, @MyID) 
END;
GO

PRINT 'Test 1'
INSERT  MyTableWithTrigger (LastUpdateDate)
VALUES  ('2011-01-01');
DELETE MyTableWithTrigger;
SELECT * FROM MyTableWithTrigger;
SELECT * FROM TransactionLog;

输出:

Msg 515, Level 16, State 2, Procedure trIUD_MyTableWithTrigger_Audit, Line 10
Cannot insert the value NULL into column 'LastUpdateDate', table 'Test.dbo.TransactionLog'; column does not allow nulls. INSERT fails.
The statement has been terminated.

[2]要纠正您的触发器(请参阅Marc的评论),请阅读Alex Kuznetsov的书(使用SQL Server进行防御性数据库编程:AmazonRed Gate - PDF)中的第192-199页。