views:

167

answers:

4

Lets say I have two tables

tblA ( tableAID INT IDENTITY(1,1), foo VARCHAR(100))

tblB ( tableBID INTIDENTITY(1,1), tableAID INT, bar varchar(100))

tblB.tableAID is a FK to tblA.

I want to insert a bunch of records (pulled from some other table in the system) into this pair of tables. I need to know what the ID from inserting into tblA is before I can insert into tblB.

Is there any way to do this without processing it row-by-row?

A: 

Can you use a temp table?

DECLARE @t TABLE (foo, int, ba VARCHAR(50))
INSERT INTO @t (foo, bar) SELECT foo, bar FROM your_other_table
ALTER TABLE @t ADD id_a INT IDENTITY(1, 1)

You should use SET IDENTITY_INSERT OFF to do this

Maybe you would want to reseed your table for doing a proper insert while the table is in production.

DBCC CHECKIDENT 'tblA' RESEED, newValue
Jhonny D. Cano -Leftware-
The target table already has data in it, so I have to get the identity values from there. Also the system will be running so I can't do a MAX(id)+1.
steve_d
I've just put more info... Explaining the RESEED stuff... maybe it can be useful for you
Jhonny D. Cano -Leftware-
A: 

Assuming that what you are really asking is how to avoid multiple round-trips to the server, then one way is to write a stored proc that does both inserts, and uses

Set @Pk = Scope_Identity()

after the first insert to get the Identity value created for that first insert and then uses that @Pk value for the second insert.

here's a simple example

Create Procedure SaveEmployee
@Name varChar(30),
@DivId Int,
@HomePhoneNumber VarChar(12),
@FaxNumber VarChar(12)
As
Set NoCount On
Declare @Pk Integer


   Insert Employees(Name, Divisionid)
   Values(@Name, @DivId)
   Set @Pk = Scope_Identity()
   -- ------------------------------------------------
   Insert PhoneNums(EmployeeId, PhoneType, PhoneNumber)
   Values(@Pk, 'Home', @HomePhoneNumber)
   -- ------------------------------------------------
   Insert PhoneNums(EmployeeId, PhoneType, PhoneNumber)
   Values(@Pk, 'Fax', @FaxNumber )

   Return 1
Charles Bretana
Actually, the script is running on the server. What I am trying to avoid is a cursor.
steve_d
A: 

In SQL Server 2005 and later, you can use the OUTPUT clause to pass the new key values into a second table:

INSERT INTO T ...
OUTPUT PrimaryKeycol, otherValues INTO ChildTable;

Other possibilies exist in SQL Server 2008 with "composable DML" and the MERGE statement.

Added in response to comment:

create table T(i int identity(1,1), j int, k int default -1);
go

merge into T using (values (1), (2)) as U(j)
on U.j = T.j
when not matched then insert (j) values (j)
output inserted.i, inserted.k;
go

drop table T;
Steve Kass
This is the closest to what I was looking for. The only drawback is that the OUTPUT clause can only access columns that are specified in the target table, which is kind of a chicken-and-egg problem - if I had something unique I was inserting into the target table, I could just join on the target table to get the inserted ID. Still, I'm accepting your answer because it taught me something new.
steve_d
Tip: If you use MERGE instead of INSERT, you can include non-mentioned columns in the OUTPUT clause. I'll add an example to my answer.
Steve Kass
A: 

It's probably past the point where it's useful, but this points out an inherent difficulty with using surrogate primary keys, and especially with using them as foreign keys in other tables. If your relationships are defined using natural keys, you know from the start what values to insert.

le dorfier
I didn't define the schema, and those who have control over the schema are in the surrogate-key camp. Anyways, I am not sure what natural key would be appropriate for the table in question - the only potential candidate userId + title, but there's not actually a business rule that titles have to be unique, and presumably one constrains the database based on the business rules, not the other way around, right?
steve_d