views:

6399

answers:

3

I am currently working in C#, and I need to insert a new record into one table, get the new primary key value, and then use that as a foreign key reference in inserting several more records. The Database is MS SQL Server 2003. All help is appreciated!

+1  A: 

The best way of doing this is the use SCOPE_IDENTITY() function in TSQL. This should be executed as part of the insert i.e.

SqlCommand cmd = new SqlCommand(@"
    INSERT INTO T (Name) VALUES(@Name)
    SELECT SCOPE_IDENTITY() As TheId", conn);
cmd.AddParameter("@Name", SqlDbType.VarChar, 50).Value = "Test";
int tId = (int)cmd.ExecuteScalar();

Alternatively you can assign SCOPE_IDENTITY() to a variable to be used in successive statements. e.g.

DECLARE @T1 int

INSERT INTO T (Name) VALUES('Test')

SELECT @T1 = SCOPE_IDENTITY() 

INSERT INTO T2 (Name, TId) VALUES('Test', @T1)
Duncan
Are all those one string, or passed in as separate strings? Your solution doesn't make that clear. Remember, I'm doing SQL queries via C#.
Zeroth
In both instances you could pass one string. For the first method you could use the SqlCommand ExecuteScaler() to get the identity of the row just inserted. You would also want to bind the hardcoded 'Test' to a @Name parameter using command.AddParameter to avoid sql injection.
Duncan
+4  A: 

The way to get the identity of the inserted row is with the SCOPE_IDENTITY() function. If you're using stored procedures then this would look something like the following to return the row identity as an output parameter.

CREATE PROCEDURE dbo.MyProcedure
(
    @RowId INT = NULL OUTPUT
)
AS

INSERT INTO MyTable
(
    Column1
    ,Column2
    ,...
)
VALUES
(
    @Param1
    ,@Param2
    ,...
);

SET @RowId = SCOPE_IDENTITY();

You can then use this value for any subsequent inserts (alternatively, if you can pass the data all into the stored procedure, then you can use it in the remainder of the procedure body).

If you're passing the SQL in dynamically then you use much the same technique, but with a single string with statement delimiters (also ; in SQL), e.g.:

var sql = "INSERT INTO MyTable (Column1, Column2, ...) VALUES (@P1, @P2, ...);" +
          "SELECT SCOPE_IDENTITY();";

Then if you execute this using ExecuteScalar you'll be able to get the identity back as the scalar result and cast it to the right type. Alternatively you could build up the whole batch in one go, e.g.

var sql = "DECLARE @RowId INT;" + 
          "INSERT INTO MyTable (Column1, Column2, ...) VALUES (@P1, @P2, ...);" +
          "SET @RowId = SCOPE_IDENTITY();" +
          "INSERT INTO MyOtherTable (Column1, ...) VALUES (@P3, @P4, ...);";

This may not be exactly the right syntax, and you may need to use SET NOCOUNT ON; at the start (my mind is rusty as I rarely use dynamic SQL) but it should get you on the right track.

Greg Beech
I prefer this method (using OUT parameters) over returning a dataset. Expecially for auto generated code.
Robert Wagner
You can skip the dataset by calling .ExecuteScalar()
Joel Coehoorn
I prefer output parameters as I think they're more self-documenting than scalar result sets, but either way would work fine.
Greg Beech
+1  A: 

If you are just using SQL then check Duncan's answer. If however you are using LINQ then you can create the entity, save it to the DB and the ID parameter will be populated automatically.

Given a user entity and a user table it might look like this:

using(var db = new DataContext()) {
    var user = new User { Name = "Jhon" };
    db.Users.InsertOnSubmit(user);
    db.SubmitChanges();
    /* At this point the user.ID field will have the primary key from the database */
}
Y Low