触发器AFTER INSERT结果无法在日志表中插入NULL

时间:2017-12-12 18:03:06

标签: sql-server database sql-server-2008 triggers

我试图在插入名为PersonalInfo的表之后编写一个触发器。 GenderID和id是表Gender和RegisterationForm的外键。

DBLog是我用来记录触发操作的表。

这就是我写的:

{{1}}

在我尝试将数据插入PersonalInfo表时执行触发器后,出现以下错误:

  

没有更新行

     

第5行中的数据未进行评估

     

错误来源:.Net SqlClient数据提供程序。

     

错误消息:无法将值NULL插入列' ActionDes',   table' .dbo.DBLog&#39 ;; column不允许nulls.INSERT失败。

     

警告:聚合或其他SET操作消除了空值。

     

声明已经终止。

确实,我不允许NULL进入我的ActionDes列,但在这里我插入数据。 NULL来自何处?问题是什么?

3 个答案:

答案 0 :(得分:3)

如果我正确理解你的触发器,对于插入表中的每一行,你要设置@variable = Max(@variable)。由于您尚未为变量赋值,因此所有变量值仍为空。

连接规则要求使用空值的连接等于空值,因此如果任何参数值为null,则@ActionDes变量以空值结束,并且当您尝试插入时,它会引发错误进入DBLog表。

答案 1 :(得分:3)

不要存储构成"假装"的字符串。插入声明。有很多方法可以插入表格,你猜测使用的格式,这是一个坏主意。

您还假设只插入了一行。这是一个严重缺陷的假设,重新编码以从inserted表中读取多行。

在日志记录中允许NULL。如果name表格中NULL字段不应为[personalInfo],请填写字段NOT NULL,不要使用触发器来强制执行检查约束

不要从日志记录表中读取id,然后递增值,将id列设为IDENTITY列,让数据库自行管理。您有一个竞争条件,您可以读取该值,然后其他一些进程插入一行,然后您尝试插入您的行并产生冲突。这是一个非常糟糕的做法。

CREATE [dbo].[InsertPersonalInfoTrigger] ON [dbo].[PersonalInfo]
  AFTER INSERT
AS
BEGIN

    INSERT INTO
      DBLog (
        ActionDes,
        ActionTime,
        ActionUser
      )
    SELECT
      ISNULL('"' + CAST(inserted.ID         AS NVARCHAR(128) + '"', 'NULL') + ' AS id, ' +
      ISNULL('"' + CAST(inserted.Name       AS NVARCHAR(128) + '"', 'NULL') + ' AS name, ' +
      ISNULL('"' + CAST(inserted.FamilyName AS NVARCHAR(128) + '"', 'NULL') + ' AS FamilyName, ' +
      ISNULL('"' + CAST(inserted.FatherName AS NVARCHAR(128) + '"', 'NULL') + ' AS FatherName, ' +
      ISNULL('"' + CAST(inserted.BirthDate  AS NVARCHAR(128) + '"', 'NULL') + ' AS BirthDate, ' +
      ISNULL('"' + CAST(inserted.GenderID   AS NVARCHAR(128) + '"', 'NULL') + ' AS GenderID, ' +
      ISNULL('"' + CAST(inserted.NationalId AS NVARCHAR(128) + '"', 'NULL') + ' AS NationalID',
      GETDATE(),
      CURRENT_USER
    FROM
      inserted
      -- There could be many rows in this table, don't pretend or assume there will only be one
END

GO

更好的是,为您从[PersonalInfo]录制的每个字段分别设置日志字段。即使这意味着拥有通用DBLog表和单独的DBLogPersonalInfo表。

CREATE [dbo].[InsertPersonalInfoTrigger] ON [dbo].[PersonalInfo]
  AFTER INSERT
AS
BEGIN

    INSERT INTO
        DBLog (
            ActionDes,
            ActionTime,
            ActionUser
        )
    VALUES (
      'INSERT INTO [dbo].[PersonalInfo]',
      GETDATE(),
      CURRENT_USER
    );

    INSERT INTO
        DBLogPersonalInfo (
            DBLogID,
            id,
            Name,
            FamilyName,
            FatherName,
            BirthDate,
            GenderID,
            NationalID
        )
    SELECT
      SCOPE_IDENTITY(),  -- The latest value from the IDENTITY column of `DBlog` (as created by THIS process).
      inserted.ID,
      inserted.Name,
      inserted.FamilyName,
      inserted.FatherName,
      inserted.BirthDate,
      inserted.GenderID,
      inserted.NationalID
    FROM
      inserted

    -- No need for `CAST` or `ISNULL`.
    -- Now you can even join DBLogPersonalInfo to PersonalInfo and compare changes (programatically)
    -- Simpler, more reliable, and more flexible...

END

GO

答案 2 :(得分:1)

有几件事:

  1. 这是一篇很好的文章," Warning: Null value is eliminated by an aggregate or other SET operation."
  2. 您的触发器是在插入后写入的,而不是在注释后更新。
  3. 最后,您假设所有值(MAX())FROM INSERTED都是非空的。这可能不是真的。尝试使用ISNULL(,N'')或COALESCE(,N'')围绕每个变量聚合@actionDes,我认为你将摆脱&# 34;错误消息:无法将值NULL插入列' ActionDes',table' .dbo.DBLog&#39 ;; column不允许nulls.INSERT失败。"
  4. 我不确定您为什么要在INSERTED上执行聚合,但我所说的我相信会解决您提到的问题。