I tend to shun surrogate keys in favour of natural keys or FKs; also, I would avoid IDENTITY
for artificial intentional. To be honest, I find myself in the minority and have often wondered myself how to achieve bulk inserts with IDENTITY
FKs.
As per Alan Barker's answer, you can utilize SCOPE_IDENTITY()
but only if you want to do this RBAR (row by agonizing row). You say, "this is a cleanup operation" so perhaps a procedural solution is acceptable.
The way I've got around the problem myself is to manually generate a sequence of potential IDENTITY
values (e.g. in a staging table) then use SET IDENTITY_INSERT TargetTable ON
to force the values in. Obviously, I need to ensure the proposed values will not actually be in use by the time the INSERT
occurs so all other users will still need to be blocked.
A couple of things to watch. Sometimes the obligatory UNIQUE
constraint on the IDENTITY
column is missing so you may need to check there are no collisions yourself. Also, I've found that the kind of person who likes surrogates can get a bit 'flustered' when the values aren't sequential (and in the positive range!) or, much worse, there is application logic that relies on a perfect sequence or has exposed the IDENTITY
values to the business (in which case 'faking' enterprise key values such as order numbers can fall fowl of real life auditors).
EDIT: reading an answer to another SO question this morning reminded me about SQL Server 2008's OUTPUT
clause to capture all the auto-generated IDENTITY
values in a table e.g.
CREATE TABLE #InsertedBooks
(
ID INTEGER NOT NULL UNIQUE, -- surrogate
isbn_13 CHAR(13) NOT NULL UNIQUE -- natural key
);
WITH InsertingBooks (isbn_13)
AS
(
SELECT '9781590597453'
UNION ALL
SELECT '9780596523060'
UNION ALL
SELECT '9780192801425'
)
INSERT INTO Books (isbn_13)
OUTPUT inserted.ID, inserted.isbn_13 -- <--
INTO #InsertedBooks (ID, isbn_13) -- <--
SELECT isbn_13
FROM InsertingBooks;
INSERT INTO AnotherTable...
SELECT T1.ID, ...
FROM #InsertedBooks AS T1...;
DROP TABLE #InsertedBooks