DATABASE

SQL Server 2008 : Using @@IDENTITY and NEWID Functions in DML Statements

1/18/2011 3:24:15 PM
Using automatically incrementing IDENTITY columns is very popular with database developers. You don’t need to explicitly calculate unique surrogate keys when inserting new data; the IDENTITY column functionality does that for you. The IDENTITY feature also allows you to specify useful Seed and Increment properties. When you use an INSERT statement to insert data into a table with an IDENTITY column defined, SQL Server will generate a new IDENTITY value. You can use the @@IDENTITY variable and the SCOPE_IDENTITY and IDENT_CURRENT functions to return the last IDENTITY value that has been generated by SQL Server. This is very useful when you need to return the key for the row that has just been inserted, back to the caller. In Example 1 a stored procedure inserts a new row into the Person.Contact table, and returns the IDENTITY value of the new record. This value can then be used in other statements.
Example 1. Using @@IDENTITY to Return the Last IDENTITY Value Generated
USE AdventureWorks;
GO
CREATE PROCEDURE sp_InsertPerson
@FirstName nvarchar(50), @LastName nvarchar(50), @PersonKey int OUT
AS
INSERT Person.Contact(FirstName, LastName, PasswordHash, PasswordSalt)
VALUES (@FirstName, @LastName, 'NO PASSWORD', 'NONE')
SET @PersonKey = @@IDENTITY
GO
DECLARE @NewKey int;
EXECUTE sp_InsertPerson 'Valentine', 'Boiarkine', @NewKey OUT
SELECT @NewKey
GO
-- Results:
-- (1 row(s) affected)
-- -----------
-- 19990

We could have used SCOPE_IDENTITY and IDENT_CURRENT in this stored procedure with the same results. The difference between these functions is scope. The @@IDENTITY variable holds the last IDENTITY value generated by SQL Server for the current session. The SCOPE_IDENTITY function returns the last IDENTITY value generated by SQL Server limited to the current session and scope only. This is useful when you are inserting into a table with a trigger bound to it, and the trigger itself causes the generation of another IDENTITY value. The IDENT_CURRENT function is not restricted by session or scope. This function returns the value of the last IDENTITY generated for a specific table. IDENT_CURRENT requires a table name parameter.

Example 2 shows the correct use of the SCOPE_IDENTITY and IDENT_CURRENT functions.

Example 2. Using @@IDENTITY, IDENT_CURRENT, and SCOPE_IDENTITY
USE AdventureWorks;
GO
INSERT Production.ScrapReason(Name)
VALUES ('Glue too weak')
SELECT @@IDENTITY as IDENTITY_VALUE,
IDENT_CURRENT('Production.ScrapReason') as IDENT_CURRENT_VALUE,
SCOPE_IDENTITY() as SCOPE_IDENTITY_VALUE
GO
-- Results:
-- (1 row(s) affected)
-- IDENTITY_VALUE IDENT_CURRENT_VALUE SCOPE_IDENTITY_VALUE
----------------- ------------------- --------------------
-- 23 23 23
-- (1 row(s) affected)

How do you decide when to use SCOPE_IDENTITY versus @@IDENTITY versus IDENT_CURRENT? When your DML statement has affected only one table, and you are querying the IDENTITY value from the same connection that made the modifications, all three will return the same value. This is the case in the previous example. However, if you query @@IDENTITY and SCOPE_IDENTITY from a new connection, they will return NULL. This is because @@IDENTITY and SCOPE_IDENTITY values are specific to the connection that executed the DML statement that changed the IDENTITY values. When would @@IDENTITY and IDENT_CURRENT functions return different values? The @@IDENTITY function returns the last IDENTITY value created in any scope within the connection, whereas IDENT_CURRENT is specific to the current scope. These functions will return different values if an IDENTITY value for a second table is changed in a nested scope—for example, by a trigger.

In Example 3, we will work with two tables: Fruit and FruitAudit; both containing IDENTITY columns named FruitID. The Fruit table contains three rows, and FruitAudit contains five. A trigger is defined on the Fruit table that will insert the new value into the FruitAudit table. When we insert a new row into Fruit, two IDENTITY values will be produced: one for the Fruit table and one for the FruitAudit table, which was modified by the trigger. The @@IDENTITY will return the last IDENTITY value regardless of scope (i.e., the IDENTITY value for the FruitAudit table). SCOPE_IDENTITY will return the IDENTITY value from the current scope, not the nested scope of the trigger. SCOPE_IDENTITY will return the IDENTITY value for the Fruit table. Try going through the following example yourself—it will give you a closer understanding of the various IDENTITY functions.

Example 3. Demonstrating the Use of Different IDENTITY Functions
CREATE TABLE dbo.Fruit (FruitID int IDENTITY PRIMARY KEY, FruitName
varchar(50))
GO
CREATE TABLE dbo.FruitAudit (FruitID int IDENTITY PRIMARY KEY, FruitName
varchar(50))
GO
CREATE TRIGGER Fruit_Inserted ON dbo.Fruit AFTER INSERT
AS
INSERT FruitAudit(FruitName) SELECT FruitName FROM INSERTED
GO
INSERT FruitAudit(FruitName) Values ('Mango'), ('Watermelon')
GO
INSERT Fruit(FruitName) Values ('Apple'), ('Banana'), ('Apricot')
GO
INSERT Fruit Values ('Pear')
- Execute the following statement from the same connection
SELECT @@IDENTITY as [@@IDENTITY], SCOPE_IDENTITY() as [SCOPE_IDENTITY],
IDENT_CURRENT('Fruit') as [IDENT_CURRENT_FRUIT],
IDENT_CURRENT('FruitAudit') as [IDENT_CURRENT_FRUITAUDIT]
-- Results (same connection):
-- @@IDENTITY SCOPE_IDENTITY IDENT_CURRENT_FRUIT IDENT_CURRENT_FRUITAUDIT
-- ---------- -------------- ------------------- ------------------------
-- 6 4 4 6
-Execute the following statement from a new connection
SELECT @@IDENTITY as [@@IDENTITY], SCOPE_IDENTITY() as [SCOPE_IDENTITY],
IDENT_CURRENT('Fruit') as [IDENT_CURRENT_FRUIT],
IDENT_CURRENT('FruitAudit') as [IDENT_CURRENT_FRUITAUDIT]
-- Results (different connection):
-- @@IDENTITY SCOPE_IDENTITY IDENT_CURRENT_FRUIT IDENT_CURRENT_FRUITAUDIT
-- ---------- -------------- ------------------- ------------------------
-- NULL NULL 4 6


Warning

Remember that @@IDENTITY is specific to the local server. When executing the INSERT statement against a remote table on a linked server, the last IDENTITY value generated for that table will not be held in @@IDENTITY. To obtain the remote @@IDENTITY value, execute a stored procedure in the context of the remote server, assigning the @@IDENTITY value to an output parameter.


Using IDENTITY_INSERT to Insert Explicit Values into IDENTITY Columns

You may wish to override the auto-generating behavior of IDENTITY columns; for example, if you have a table with an IDENTITY column defined, but you have deleted many rows in this table. The IDENTITY value will never decrement itself, and therefore you will experience gaps in your key values. If this behavior is undesirable, you can use the SET IDENTITY_INSERT [database_name]. [schema_name].table_name ON | OFF statement to enable IDENTITY_INSERT functionality. Example 4 demonstrates the proper use of IDENTITY_INSERT.

Example 4. Using IDENTITY_INSERT
CREATE TABLE Fruit (FruitID int IDENTITY PRIMARY KEY, FruitName nvarchar(50))
GO
INSERT Fruit (FruitName) VALUES ('Apple'), ('Pomegranate'), ('Kiwifruit')
SELECT * FROM Fruit
GO
-- Results:
-- FruitID FruitName
-- ----------- -------------
-- 1 Apple
-- 2 Pomegranate
-- 3 Kiwifruit
DELETE Fruit WHERE FruitID = 1 OR FruitID = 3
INSERT Fruit (FruitName) VALUES ('Jackfruit'), ('Mango')
SELECT * FROM Fruit

GO
-- Results (Note that we now have gaps in key values):
-- FruitID FruitName
-- ----------- -----------
-- 2 Pomegranate
-- 4 Jackfruit
-- 5 Mango
INSERT Fruit (FruitID, FruitName) VALUES (1, 'Seaberry'), (3, 'Durian')
GO
-- Results:
-- Msg 544, Level 16, State 1, Line 1
-- Cannot insert explicit value for identity column in table 'Fruit' when
IDENTITY_INSERT is set to OFF.

SET IDENTITY_INSERT Fruit ON
INSERT Fruit (FruitID, FruitName) VALUES (1, 'Seaberry'), (3, 'Durian')
SET IDENTITY_INSERT Fruit OFF
SELECT * FROM Fruit
GO
-- Results:
-- FruitID FruitName
-- ----------- -----------
-- 1 Seaberry
-- 2 Pomegranate
-- 3 Durian
-- 4 Jackfruit
-- 5 Mango


Tip

Remember that the IDENTITY value for a table is incremented even if an INSERT statement fails. This means failed DML statements and transactions will cause gaps in the IDENTITY column. You can correct the gaps by setting IDENTITY_INSERT to ON, and manually inserting new data with explicitly specified IDENTITY values.


Configuring & Implementing...: Using the DBCC CHECKIDENT Statement to View and Modify IDENTITY Values

SQL Server automatically maintains IDENTITY values for tables with IDENTITY columns defined. However, you may wish to reset the IDENTITY value back to its initial value, or to a specified value. You may also wish to view the current IDENTITY value for a table. These tasks are performed using the DBCC CHECKIDENT statement. The statement is used as follows:

DBCC CHECKIDENT (table_name, NORESEED | RESEED, [new_reseed_value])

When NORESEED is specified, the IDENTITY value for the table is not reset. The current IDENTITY value and the maximum value contained in the IDENTITY column are returned. If the two values are not the same, you may wish to reset the IDENTITY value to avoid gaps in IDENTITY column values.

When RESEED is specified, the identity value for the table is reset to either the maximum value contained in the IDENTITY column, or a value you specified for new_reseed_value. If the table contains no data, the identity value for the table is reset to its initial value.

The DBCC CHECKIDENT statement is especially useful after you have used IDENTITY_INSERT. For example, you may have just inserted a number of explicit IDENTITY values into a table. You can now use IDENTITY_INSERT to reset the IDENTITY value to the next logical value, so that the next record receives the correct IDENTITY value.

You may want to use the DBCC CHECKIDENT statement when you have emptied a table. Although the table is empty, the IDENTITY value remains at what it was when the table had data. You can now use the DBCC CHECKIDENT statement to reset the IDENTITY back to its initial value (usually 1).


Using the NEWID Function

Columns of type uniqueidentifier store globally unique identifier (GUID) data. Sometimes you need to insert values into these columns. The NEWID function can be used to generate a new value of type uniqueidentifier. This value is guaranteed to be globally unique. Example 5 demonstrates the use of the NEWID function.

Example 5. Using the NEWID Function
CREATE TABLE SpareParts (PartID uniqueidentifier PRIMARY KEY,
PartName nvarchar(50))
GO

INSERT SpareParts(PartID, PartName)
VALUES
(NEWID(), 'Fibre Channel Card'),
(NEWID(), 'Diagnostics Card')

SELECT * FROM SpareParts
-- Results:
-- PartID PartName
-- ------------------------------------ -------------------
-- AD4A4B10-A62B-4322-905A-9A5C51EB6010 Fibre Channel Card
-- 8750D92A-9FAC-4DC2-AB92-FB7A9C76E333 Diagnostics Card

Warning

Do not confuse the @@IDENTITY and NEWID functions. The @@IDENTITY function holds the value of the last IDENTITY value generated by the server for the current user session. The NEWID() function generates and returns a new GUID.


Exercise. Working With Identity and Uniqueidentifier Columns

In this exercise, we will practice working with IDENTITY and UNIQUEIDENTIFIER data.

Before you begin, you must have the following software installed on your computer:

  • SQL Server 2008 (a free trial is available for download)

  • AdventureWorks sample database

We will be adding rows to the Purchasing.ShipMethod table in the AdventureWorks database.

  1. Open SQL Server Management Studio. Click Start | All Programs | Microsoft SQL Server 2008 | SQL Server Management Studio.

  2. Create a new query against the AdventureWorks database.

  3. Create a table named Stars by executing the following statement:

    CREATE TABLE Stars
    (StarID uniqueidentifier PRIMARY KEY,
    StarName nvarchar(50))
  4. Use the NEWID function to insert two rows into the Stars table with star names ‘Deneb’ and ‘Pollux’. Write and execute your statement as follows:

    INSERT Stars (StarID, StarName)
    VALUES (NEWID(), 'Deneb'),
    (NEWID(), 'Pollux')
  5. Use the SELECT statement to view the data contained in a table. Yours statement and results should look as follows.

    SELECT * FROM Stars
    -- Results:
    -- StarID StarName
    -- ------------------------------------ ---------
    -- 57559159-5B56-4346-9871-0ED3A228D6C1 Pollux
    -- 1861C6D2-AB79-431B-A5D9-8E42FDE1F4B5 Deneb
  6. Create a new query against the AdventureWorks database.

  7. Create a table named Planets by executing the following statement:

    CREATE TABLE Planets
    (PlanetID int IDENTITY PRIMARY KEY,
    PlanetName nvarchar(50))
  8. Use the INSERT statement with PlanetName only in the ColumnList to insert three rows into the Planets table: ‘Mars’, ‘Earth’, and ‘Jupiter’. Examine the data contained in the table, and the value of the @@IDENTITY variable. Write and execute your statement as follows:

    INSERT Planets (PlanetName)
    VALUES('Mars'), ('Earth'), ('Jupiter')
    SELECT * FROM Planets

    SELECT @@IDENTITY as [Last IDENTITY]
    -- Results:
    -- PlanetID PlanetName
    -- ----------- -----------
    -- 1 Mars
    -- 2 Earth
    -- 3 Jupiter
    -- Last IDENTITY
  9. Delete the record with PlanetID = 2. Examine the data contained in the Planets table. Write and execute your statement as follows:

    DELETE Planets WHERE PlanetID = 2
    SELECT * FROM Planets
    -- Results:
    -- PlanetID PlanetName
    -- ----------- -----------
    -- 1 Mars
    -- 3 Jupiter
  10. Using IDENTITY_INSERT, insert the planet Venus with a PlanetID of 2. Write and execute your statement as follows:

    SET IDENTITY_INSERT Planets ON
    INSERT Planets (PlanetID, PlanetName)
    VALUES(2, 'Venus')
    SET IDENTITY_INSERT Planets OFF
    SELECT * FROM Planets
    -- Results:
    -- PlanetID PlanetName
    -- ----------- -----------
    -- 1 Mars
    -- 2 Venus
    -- 3 Jupiter
Other  
  •  SQL Server 2008 : Using Advanced Functionality with DML - Introduction
  •  Defensive Database Programming with SQL Server: The Ticket-Tracking System (part 2) - Removing the performance hit of ON UPDATE CASCADE
  •  Defensive Database Programming with SQL Server: The Ticket-Tracking System (part 1) - Enforcing business rules using constraints only
  •  SQL Server 2008 : Working with DML Queries - Using the MERGE Statement
  •  Defensive Database Programming with SQL Server : Client-side Error Handling
  •  SQL Server 2008 : Working with DML Queries - Using the DELETE Statement
  •  Programming Microsoft SQL Server 2005: Using Data Mining Extensions (part 2) - Data Mining Predictions Using DMX
  •  Programming Microsoft SQL Server 2005: Using Data Mining Extensions (part 1) - Data Mining Modeling Using DMX
  •  SQL Server 2008 : Working with DML Queries - Using the UPDATE Statement (part 2)
  •  SQL Server 2008 : Working with DML Queries - Using the UPDATE Statement (part 1)
  •  
    Video
    Top 10
    SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
    The latest Audi TT : New angles for TT
    Era of million-dollar luxury cars
    Game Review : Hearthstone - Blackrock Mountain
    Game Review : Battlefield Hardline
    Google Chromecast
    Keyboards for Apple iPad Air 2 (part 3) - Logitech Ultrathin Keyboard Cover for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 2) - Zagg Slim Book for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 1) - Belkin Qode Ultimate Pro Keyboard Case for iPad Air 2
    Michael Kors Designs Stylish Tech Products for Women
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    Popular Tags
    Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone