PRB: Guarantee @@IDENTITY Value on a Per Table Basis

Last reviewed: April 10, 1997
Article ID: Q163446
The information in this article applies to:
  • Microsoft SQL Server, versions 6.0 and 6.5

SYMPTOMS

SQL Server documents and treats the @@IDENTITY variable as a global variable. The following is an excerpt from the SQL Server Books Online:

   To get the last identity value, use the @@IDENTITY global variable. This
   variable is accurate after an insert into a table with an identity
   column; however, this value is reset after an insert into a table
   without an identity column occurs.

Improperly relying on this behavior may quickly cause changes in your application. For example, suppose you do an insert into a table and then check the @@IDENTITY value, to do foreign key (FK) inserts. The administrator then adds an insert trigger to the table to perform an insert into the audit table. Now the @@IDENTITY value has been altered, and is no longer the proper @@IDENTITY for FK inserts.

This problem may compound itself when you start adding nested triggers or stored procedures.

WORKAROUND

There is no way to implement a solid workaround without making code changes. However, you can employ one of following two strategies:

Method One

Use the method documented in the SQL Server 4.21a Transact-SQL Guide to implement a counter or identity column.

To use this method of "Using a Sequence Number Table," see page 165 of the SQL Server 4.21a Transact-SQL Guide. The following is an excerpt from that guide:

   Here you use a separate table to maintain the highest sequence number.
   This approach ensures that sequence numbers are assigned in sequential
   order, without any holds, by effectively single-threading inserts.

Be careful, because this method may cause concurrency contention issues. The guide also describes several other methods, like using the @@DBTS value, a timestamp, and so on.

Method Two

Design your own series of procedures and triggers to maintain the @@IDENTITY variable. The basic design is to create a temporary table before you begin working in the database. Then, for every table that contains an identity column, add an INSERT trigger that places the @@IDENTITY value in your temporary table. This value is then retrieved later by selecting back the proper value. If you are using transactions, be aware of their ramifications on the temporary table inserts.

The following script shows how to retain the last @@IDENTITY value assigned to a specific table:

   select @@VERSION
   go

   use pubs
   go

   set nocount on
   go


   print ''
   print 'Create the sample tables...'
   print ''
   go


   drop table tblAudit
   go

   create table tblAudit
   (
      iID   int   identity(2500,1),
      strData     varchar(10)
   )
   go

   drop table tblIdentity
   go

   create table tblIdentity
   (
      iID   int   identity(1,1),
      strData     varchar(10)
   )
   go


   print ''
   print 'Create the sample procedures and triggers...'
   print ''
   go

   create table #tblIdentity
   (
   iID   int,
   strTable   varchar(30)
   )
   go

   create trigger trgIdentity on tblIdentity for INSERT
   as
      insert into #tblIdentity values (@@IDENTITY, 'tblIdentity')
      insert into tblAudit values ('Audit entry')

   go

   create trigger trgAudit on tblAudit for INSERT
   as
      insert into #tblIdentity values (@@IDENTITY, 'tblAudit')
   go

   drop procedure sp_Insert
   go

   create procedure sp_Insert
   as
      insert into tblIdentity values('Test')

      print ' '
      print 'Simple reliance on the @@IDENTITY after the execution would
      incorrectly yield'
      print 'FK references of tblIdentity would be incorrect'
      print ' '
      select '@@IDENTITY' = @@IDENTITY

   go


   drop procedure sp_GetIdentity
   go

   create procedure sp_GetIdentity @strTable varchar(30)
   as
   declare   @iIdentity int

   select @iIdentity = iID
      from #tblIdentity
      where strTable = @strTable

   return @iIdentity
   go

   drop table #tblIdentity
   go


   print ''
   print 'Show the process in action'
   print ''

   create table #tblIdentity
   (
   iID   int,
   strTable   varchar(30)
   )
   go


   exec sp_Insert
   go


   print ' '
   print 'After execution you can get a specific table value...'
   print ' '
   go

   declare @iIdentity int

   exec @iIdentity = sp_GetIdentity 'tblIdentity'
   select 'tblIdenity' = @iIdentity

   exec @iIdentity = sp_GetIdentity 'tblAudit'
   select 'tblAudit' = @iIdentity

   drop table #tblIdentity
   go


Additional query words:
Keywords : kbusage SSrvGen
Version : 6.0 6.5
Platform : WINDOWS
Issue type : kbprb


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: April 10, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.