如何防止试图设置标识列的SQL触发器插入的记录

时间:2019-04-26 12:27:53

标签: sql sql-server triggers uniqueidentifier database-trigger

我正在尝试创建一个“历史”表,该表在每次更新源表中的一行时都会更新。

这是我用来创建历史记录表的(SQL Server)代码:

DROP TABLE eventGroup_History 

SELECT 
    CAST(NULL AS UNIQUEIDENTIFIER) AS NewId, 
    CAST(NULL AS varchar(255)) AS DoneBy, 
    CAST(NULL AS varchar(255)) AS Operation, 
    CAST(NULL AS datetime) AS DoneAt, 
    * 
INTO 
    eventGroup_History 
FROM 
    eventGroup 
WHERE 
    1 = 0
GO

ALTER TABLE eventGroup_History 
ALTER COLUMN NewId UNIQUEIDENTIFIER NOT NULL
go

ALTER TABLE eventGroup_History 
ADD PRIMARY KEY (NewId)
GO

ALTER TABLE eventGroup_History 
ADD CONSTRAINT DF_eventGroup_History_NewId DEFAULT NewSequentialId() FOR NewId
GO

触发器是这样创建的:

drop trigger eventGroup_LogUpdate
go

create trigger eventGroup_LogUpdate 
on dbo.eventGroup
for update
as
  declare @Now as DateTime = GetDate()
  set nocount on

  insert into eventGroup_History
      select @Now, SUser_SName(), 'update-deleted', *
      from deleted

  insert into eventGroup_History
      select SUser_SName(), 'update-inserted', @Now, *
      from inserted
go

exec sp_settriggerorder @triggername = 'eventGroup_LogUpdate', @order = 'last', @stmttype = 'update'

但是当我在SQL Server Management Studio中更新一行时,会收到一条消息:

  

第2行中的数据未提交。
  错误源:.Net SqlClient数据提供程序。
  错误消息:从字符串转换为uniqueidentifier时转换失败。

enter image description here

我认为触发器正在尝试将SUserSName()作为行的第一列插入,但这就是PK NewId:

enter image description here

表中没有其他的uniqueidentifier列。

如果我从SQL Management Studio的编辑网格中添加行,则无需添加NewId值即可添加该行。

那么,为什么SQL Server触发器试图用NewId子句中的第一项填充INSERT INTO而不是跳过它以让常规IDENTITY操作提供值? >

(如何阻止这种情况发生,以使触发器起作用?)

2 个答案:

答案 0 :(得分:3)

如果要在NewId列上使用默认值,则需要在INSERT语句中显式列出列名称。默认情况下,除非您提供足够的信息,否则SQL Server将按SELECT中列出的顺序插入列。为了避免这种意料之外的结果,显式列出各列是一种最佳实践,一种方法还是另一种方法。

所以您的陈述最终看起来像这样:

INSERT INTO eventGroup_History
  (
    DoneBy,
    Operation,
    DoneAt,
    <All the other columns that are masked by the *>
  )
SELECT....

答案 1 :(得分:3)

因为自动跳过仅适用于IDENTITY列-设置了NewSequentialId()约束的GUID列在许多方面的行为与IDENTITY类似,但不适用于此行为。

通过显式指定INSERT的列,可以实现所需的功能。