views:

1331

answers:

6

I have a stored procedure with multiple insert/select statements. Let's say I'm using the first insert to populate a "Manager" table. On insert, a ManagerId (incremented automatically) is added, but not referenced in the insert statement. I then wish to use the ManagerId from this table to insert a row into another table, where ManagerId is a foreign key. Sample code as follows..

USE [TEST]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sptInsertNewManager]
    -- Add the parameters for the stored procedure here
    @FName varchar(50),
    @LName varchar(50),
    @EMail varchar(100),   
    @UserRoleID int,
    @LANUserID varchar(25), 
    @GroupID int
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
INSERT INTO [Manager]           
          ([FName], 
           [LName],
           [Email], 
           [UserRoleID],
           [LANUserID],          
           [ActiveFlag],
           [GroupID]
)
     VALUES
      (@FName
      ,@LName
      ,@EMail
            ,@UserRoleID
            ,@LANUserID
      ,1 
            ,@GroupID);

COMMIT

SELECT ManagerId FROM [Manager] AS newManager WHERE LANUserID = @LANUserID; 
     --also insert into Users table.
    INSERT INTO [dbo].[aspnet_Users] (
     [UserId],
     [UserName],
     [LoweredUserName],
     [ManagerId]
    )
     VALUES (
      NEWID(),
      @LANUserID,
      LOWER(@LANUserID),
      newManager)
END

This, obviously, does not work. This was my attempt at solving this. I'm fairly new to SQL, so any help with this problem would be greatly appreciated.

+5  A: 

use scope_identity() after your insert to capture the most recent single identity value from within your current scope:

DECLARE @ID     int


INSERT ......
SELECT @ID=scope_identity()

use @ID wherever you need it

note: SCOPE_IDENTITY() is preferred over the older @@IDENTITY because it gives the last Identity value in the current scope, which avoids issues from triggers that insert into log tables (with identities).

However, if you need multiple identity values (inserting a set of rows), use OUTPUT and INTO:

declare @test table (RowID  int identity(1,1) primary key not null, RowValue varchar(10) null)
declare @OutputTable table (RowID int not null)

insert into @test (RowValue)
    OUTPUT INSERTED.RowID
    INTO @OutputTable
    SELECT 'A'
    UNION SELECT 'B'
    UNION SELECT 'C'
    UNION SELECT 'D'
    UNION SELECT 'E'


select * from @OutputTable

the output:

(5 row(s) affected)
RowID
-----------
1
2
3
4
5

(5 row(s) affected)
KM
A: 

You can use @@Identity global variable to retrieve last inserted identity value

SELECT @ManagerId=@@Identity

you can also use Scope_Identity and IDENT_Current functions

Beatles1692
@@dentity will give you the last identity, even if it was not from the insert in your scope (like if the insert fires a trigger)
KM
Agree with KM, I would never use @@identity because sooner or later you will have data integrity problems. SAme with ident_current (you can get the identity another process just inserted after yours). If I want the identity I just inserted I use scope_identity or on a newer version you can use the output clause (Not sure if this came in in with 2005 or 2008 as we skipped 2005) which has the advantage of returning the whole set of identity values if you ran a set-based mulitple record insert.
HLGEM
+2  A: 

for MS Sql Server

Whenever you insert a record and it has auto-increment column (Identity Column in ms-sql parlance) you can user this to retrieve the id for the row you inserted:

@id = SCOPE_IDENTITY()

this ensures that you get the identity column value that your insert produced, not by the other users during the time your code was running (between insert and getting identity by *scope_identity()*)

TheVillageIdiot
+2  A: 

Yuck... you have your proc and transaction all twisted up...

You want these two inserts to occur within the same transaction (which I am deducing from the COMMIT statement in the middle of your original proc). So you need to appropriately place a BEGIN TRANSACTION and COMMIT statement around all of your INSERT and SELECT statements to isolate the data change.

Consider the following:

CREATE PROCEDURE [dbo].[sptInsertNewManager]
    -- Add the parameters for the stored procedure here
    @FName varchar(50),
    @LName varchar(50),
    @EMail varchar(100),   
    @UserRoleID int,
    @LANUserID varchar(25), 
    @GroupID int
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

DECLARE @ManagerID  INT

BEGIN TRANSACTION    

    -- Insert statements for procedure here
INSERT INTO [Manager]           
          ([FName], 
           [LName],
           [Email],     
           [UserRoleID],
           [LANUserID],          
           [ActiveFlag],
           [GroupID]
)
     VALUES
                (@FName
                ,@LName
                ,@EMail
            ,@UserRoleID
            ,@LANUserID
                ,1 
            ,@GroupID);

-- Collect the ID you just created
SELECT @ManagerID = SCOPE_IDENTITY()


        --also insert into Users table.
    INSERT INTO [dbo].[aspnet_Users] (
        [UserId],
        [UserName],
        [LoweredUserName],
        [ManagerId]
    )
        VALUES (
                NEWID(),
                @LANUserID,
                LOWER(@LANUserID),
                @ManagerID)   -- This is the new identity you just created

COMMIT

END
Jeff Fritz
+1  A: 

ALso do not commit in the middle of the transaction, you want both inserts to rollback if one fails. Read in Books online about transaction processing.

HLGEM
A: 

here is what we can do. we can return the identity column like SELECT @@identity as 'Identity' and read this identity value in code like int ID = Convert.ToInt32(cmdinsert.ExecuteScalar());

Alok

Alok Kumar