SCOPE_IDENTITY而不是插入触发器解决方法

时间:2009-09-16 16:31:35

标签: sql sql-server tsql triggers

好的,我有一个没有自然键的表,只有一个整数标识列作为它的主键。我想插入和检索标识值,但也使用触发器来确保始终设置某些字段。最初,设计是使用而不是插入触发器,但这会破坏scope_identity。 insert语句中的output子句也被insert而不是insert触发器打破。所以,我想出了一个替代计划,想知道我打算做什么有明显的错误:

开始做出例子:

    CREATE TABLE [dbo].[TestData] (
    [TestId] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
    [Name] [nchar](10) NOT NULL)

    CREATE TABLE [dbo].[TestDataModInfo](
    [TestId] [int] PRIMARY KEY NOT NULL,
    [RowCreateDate] [datetime] NOT NULL)

    ALTER TABLE [dbo].[TestDataModInfo]  WITH CHECK ADD  CONSTRAINT
    [FK_TestDataModInfo_TestData] FOREIGN KEY([TestId])
    REFERENCES [dbo].[TestData] ([TestId]) ON DELETE CASCADE

CREATE TRIGGER [dbo].[TestData$AfterInsert]
   ON [dbo].[TestData]
   AFTER INSERT
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    INSERT INTO [dbo].[TestDataModInfo]
           ([TestId],
            [RowCreateDate])
        SELECT
            [TestId],
            current_timestamp
        FROM inserted

    -- Insert statements for trigger here

END

结束人为的例子。

不,我不是为一个小日期字段做这个 - 这只是一个例子。

我想要确保设置的字段已移至单独的表(在TestDataModInfo中),触发器确保它已更新。这有效,它允许我在插入后使用scope_identity(),并且看起来是安全的(如果我的后触发失败,我的插入失败)。这是不好的设计,如果是这样,为什么?

4 个答案:

答案 0 :(得分:3)

正如您所提到的,SCOPE_IDENTITY是针对这种情况而设计的。与@@ IDENTITY不同,它不受AFTER触发代码的影响。

除了使用存储过程之外,这没关系。

我使用AFTER触发器进行审核,因为它们很方便......也就是说,写入触发器中的另一个表。

修改:SCOPE_IDENTITY and parallelism in SQL Server 2005 cam have a problem

答案 1 :(得分:0)

您是否尝试过使用OUTPUT来取回值?

答案 2 :(得分:0)

答案 3 :(得分:0)

你可以使用INSTEAD OF触发器,只需在插入主表之后捕获值,然后在触发结束时将Scope_Identity()欺骗到@@Identity

-- Inside of trigger
SET NOCOUNT ON;
INSERT dbo.YourTable VALUES(blah, blah, blah);
SET @YourTableID = Scope_Identity();

-- ... other DML that inserts to another identity-bearing table

-- Last statement in trigger
SELECT YourTableID INTO #Trash FROM dbo.YourTable WHERE YourTableID = @YourTableID;

或者,这是一个不使用任何读取的备用最终语句,但如果执行用户没有权限,可能会导致权限问题(尽管有解决方案)。

SET @SQL =
   'SELECT identity(smallint, ' + Str(@YourTableID) + ', 1) YourTableID INTO #Trash';
EXEC (@SQL);

请注意,Scope_Identity()在某些情况下可能会在具有INSTEAD OF触发器的表上返回NULL,即使您使用此欺骗方法也是如此。但你至少可以使用@@Identity获得价值。这可以使MS Access ADP项目在中断后再次开始正常工作,因为您在前端插入的表上放置了一个触发器。

另外,请注意,任何并行操作都会导致@@IdentityScope_Identity()返回不正确的值 - 因此请使用OPTION (MAXDOP 1)TOP 1或单行{{1 }}子句来解决这个问题。