The only way for you to reliably show an IDENTITY
value on your application's form is to INSERT IT FIRST. IDENT_CURRENT
might seem to help you when you're the only person testing it, but I can assure you this will fall apart quite quickly once multiple users are using your application. It is very easy to prove, too. Create the following table:
CREATE TABLE dbo.whatever(ID INT IDENTITY(1,1), blat CHAR(1));
Now, in two separate Management Studio windows, first run this code, which simulates what you'd be showing on the form, if you follow the accepted answer and what you said "works":
SELECT IDENT_CURRENT('dbo.whatever');
Note the output (both should be 1
). This is correct. SO FAR.
Now, in one window, run this:
INSERT dbo.whatever(blat) SELECT 'x';
SELECT SCOPE_IDENTITY();
The output should be 1
(which, again, is correct SO FAR).
Now, in the other window, run the same thing, but change x
to y
. This output is now 2
. UH-OH. This does not match what you showed this user on their form. You can also validate that by seeing there are two rows in the table, with 1
and 2
as the IDENTITY
values:
SELECT ID, blat FROM dbo.whatever;
The right way to do this, and the only way to do it, is to insert a row, retrieve the value, and then show it on the form. If you need to show them some surrogate value beforehand (no idea why you would need to do this, or why your end users need to know this value no matter when you retrieve it - why do users care what the ID is?), then create a separate table and generate your IDENTITY
values there.
CREATE TABLE dbo.dummy_table(ID INT IDENTITY(1,1) PRIMARY KEY);
GO
CREATE TABLE dbo.real_table(ID INT PRIMARY KEY, ...other columns...);
GO
Now, when you want to show the "next" ID on the form, you can do so using the following:
INSERT dbo.dummy_table DEFAULT VALUES;
SELECT SCOPE_IDENTITY();
Then when the user fills out the rest of the information, you can insert into dbo.real_table
and include the ID
column in the insert list, using the value you retrieved from dbo.dummy_table
. Note that this will still end up with gaps in the event that a user saw an ID and didn't click save, but that ID is now meaningless as it never made it into the real table - and there is no possibility for anyone else to have seen it (unlike what can happen with IDENT_CURRENT
, MAX+1
and other ill-conceived "check the value first" techniques).
If your actual goal is to insert three copies of the book into another table, the solution is quite simple, and still requires that you insert the book first. Let's assume you have parameters to represent the name of the book and the number of copies (as well as other parameters I'm sure):
DECLARE @Copies INT, @name NVARCHAR(32);
SELECT @Copies = 3, @name = N'Moby Dick';
Now, we can insert into the dbo.Books
table, and use the output to insert multiple rows into the other table (dbo.Accession
?). No need to blindly guess at the "next" value of BookID
first.
DECLARE @BookID INT;
INSERT dbo.Books(name, copies, whatever...) SELECT N'Moby Dick', 3, ...;
SELECT @BookID = SCOPE_IDENTITY();
INSERT dbo.Accession(AccessionID, BookID)
SELECT rn, @BookID
FROM
(
SELECT TOP (@Copies) rn = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.columns ORDER BY [object_id]
) AS y;
This uses a trick to generate multiple rows from catalog views, but you can also use a built-in Numbers
table, if you have one, for improved efficiency (and less restrictive permissions).