如何防止 SQL 触发器插入的记录试图设置标识列
How can I prevent a record inserted by an SQL trigger attempting to set the identity column
我正在尝试创建一个 'history' table,每次更新源 table 上的一行时都会更新。
这是我用来创建历史记录的(SQL 服务器)代码 table:
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 中更新一行时,我收到一条消息:
The data in row 2 was not committed.
Error Source: .Net SqlClient Data Provider.
Error Message: Conversion failed when converting from a character string to uniqueidentifier.
我认为触发器试图将 SUserSName()
作为该行的第一列插入,但这是 PK NewId:
table 中没有其他 uniqueidentifier 列。
如果我从 SQL Management Studio 的编辑网格中添加行,则无需指定 NewId 值即可添加该行。
那么,为什么 SQL 服务器触发器试图用 INSERT INTO
子句中的第一项填充 NewId
而不是跳过它以让正常的 IDENTITY
操作提供一个值?
(以及如何阻止这种情况发生,以便触发器起作用?)
如果要在 NewId
列上使用默认值,则需要在 INSERT
语句中明确列出列名。默认情况下,SQL 服务器将按照它们在 SELECT
中列出的顺序插入列,除非您为其提供足够的信息以执行其他操作。明确列出列是一种最佳做法,无论是哪种方式,都是为了避免出现这种意外结果。
因此您的语句最终将如下所示:
INSERT INTO eventGroup_History
(
DoneBy,
Operation,
DoneAt,
<All the other columns that are masked by the *>
)
SELECT....
因为自动跳过仅适用于 IDENTITY 列 - 使用 NewSequentialId() 约束设置的 GUID 列在许多方面与 IDENTITY 的行为相似,但这一列不同。
您可以通过明确指定 INSERT 的列来实现您正在寻找的内容。
我正在尝试创建一个 'history' table,每次更新源 table 上的一行时都会更新。
这是我用来创建历史记录的(SQL 服务器)代码 table:
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 中更新一行时,我收到一条消息:
The data in row 2 was not committed.
Error Source: .Net SqlClient Data Provider.
Error Message: Conversion failed when converting from a character string to uniqueidentifier.
我认为触发器试图将 SUserSName()
作为该行的第一列插入,但这是 PK NewId:
table 中没有其他 uniqueidentifier 列。
如果我从 SQL Management Studio 的编辑网格中添加行,则无需指定 NewId 值即可添加该行。
那么,为什么 SQL 服务器触发器试图用 INSERT INTO
子句中的第一项填充 NewId
而不是跳过它以让正常的 IDENTITY
操作提供一个值?
(以及如何阻止这种情况发生,以便触发器起作用?)
如果要在 NewId
列上使用默认值,则需要在 INSERT
语句中明确列出列名。默认情况下,SQL 服务器将按照它们在 SELECT
中列出的顺序插入列,除非您为其提供足够的信息以执行其他操作。明确列出列是一种最佳做法,无论是哪种方式,都是为了避免出现这种意外结果。
因此您的语句最终将如下所示:
INSERT INTO eventGroup_History
(
DoneBy,
Operation,
DoneAt,
<All the other columns that are masked by the *>
)
SELECT....
因为自动跳过仅适用于 IDENTITY 列 - 使用 NewSequentialId() 约束设置的 GUID 列在许多方面与 IDENTITY 的行为相似,但这一列不同。
您可以通过明确指定 INSERT 的列来实现您正在寻找的内容。