SCOPE_IDENTITY And Instead of Insert Trigger work-

2019-05-12 06:22发布

问题:

OK, I have a table with no natural key, only an integer identity column as it's primary key. I'd like to insert and retrieve the identity value, but also use a trigger to ensure that certain fields are always set. Originally, the design was to use instead of insert triggers, but that breaks scope_identity. The output clause on the insert statement is also broken by the instead of insert trigger. So, I've come up with an alternate plan and would like to know if there is anything obviously wrong with what I intend to do:

begin contrived example:

    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

End contrived example.

No, I'm not doing this for one little date field - it's just an example.

The fields that I want to ensure are set have been moved to a separate table (in TestDataModInfo) and the trigger ensures that it's updated. This works, it allows me to use scope_identity() after inserts, and appears to be safe (if my after trigger fails, my insert fails). Is this bad design, and if so, why?

回答1:

As you mentioned, SCOPE_IDENTITY is designed for this situation. It's not affected by AFTER trigger code, unlike @@IDENTITY.

Apart from using stored procs, this is OK.

I use AFTER triggers for auditing because they are convenient... that is, write to another table in my trigger.

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



回答2:

HAve you tried using OUTPUT to get the value back instead?



回答3:

Have you tried using:

SELECT scope_identity();

http://wiki.alphasoftware.com/Scope_Identity+in+SQL+Server+with+nested+and+INSTEAD+OF+triggers



回答4:

You can use an INSTEAD OF trigger just fine, by in the trigger capturing the value just after the insert to the main table, then spoofing the Scope_Identity() into @@Identity at the end of the trigger:

-- 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;

Or, here's an alternate final statement that doesn't use any reads, but may cause permission issues if the executing user doesn't have rights (though there are solutions to this).

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

Note that Scope_Identity() may return NULL on a table with an INSTEAD OF trigger on it in some cases, even if you use this spoofing method. But you can at least get the value using @@Identity. This can make MS Access ADP projects start working right again after breaking because you put a trigger on a table that the front end inserts to.

Also, be aware that any parallelism at all can make @@Identity and Scope_Identity() return incorrect values—so use OPTION (MAXDOP 1) or TOP 1 or a single-row VALUES clause to defeat this problem.