How Do You Tell What Next Identity Column Will Be? - sql-server

Is there a tsql query to tell what SQL server identity column value it expects to use for the next row insert?
Edited to add:
I deleted and recreated a table with
[personID] [int] IDENTITY(1,1) NOT NULL
as part of my CREATE TABLE command. I've also attempted to reseed identity columns while removing all information in that table and that hasn't always worked. It got me to wondering if there was a way to see what SQL expected to use for your next identity column number.

You probably want to use SCOPE_IDENTITY not ##IDENTITY to restrict it to the identity value in the current scope. This avoids getting new identity values inserted by triggers into other tables and not the table you just inserted into.
But you can calculate what the next identity value is
SELECT IDENT_CURRENT('mytable') + IDENT_INCR('mytable') FROM mytable
The problem is you aren't guaranteed that is the value.
You'd have to have a lock such that other inserts are denied on the table when running it to ensure the value is accurate. Also after you run out of 32 bit integers I don't know what the logic is. I don't know whether it rolls over or fails.
Edit:
I just tested this (see below for SQL) and it doesn't return the correct value when there is no data.
And reseeding with DBCC CHECKIDENT ('tablename', RESEED, 200) actually resulted in the next value being 201 not 200.
CREATE TABLE willtest (myid integer IDENTITY(1,1), myvalue varchar(255))
SELECT IDENT_CURRENT('willtest') + IDENT_INCR('willtest')
INSERT INTO willtest (myvalue)
VALUES ('1')
INSERT INTO willtest (myvalue)
VALUES ('2')
INSERT INTO willtest (myvalue)
VALUES ('3')
INSERT INTO willtest (myvalue)
VALUES ('4')
INSERT INTO willtest (myvalue)
VALUES ('5')
INSERT INTO willtest (myvalue)
VALUES ('6')
INSERT INTO willtest (myvalue)
VALUES ('7')
INSERT INTO willtest (myvalue)
VALUES ('8')
SELECT IDENT_CURRENT('willtest') + IDENT_INCR('willtest')
DBCC CHECKIDENT ('willtest', RESEED, 200)
SELECT IDENT_CURRENT('willtest') + IDENT_INCR('willtest')
INSERT INTO willtest (myvalue)
VALUES ('200')
INSERT INTO willtest (myvalue)
VALUES ('201')
INSERT INTO willtest (myvalue)
VALUES ('202')
INSERT INTO willtest (myvalue)
VALUES ('203')
INSERT INTO willtest (myvalue)
VALUES ('204')
INSERT INTO willtest (myvalue)
VALUES ('205')
INSERT INTO willtest (myvalue)
VALUES ('206')
INSERT INTO willtest (myvalue)
VALUES ('207')
SELECT IDENT_CURRENT('willtest') + IDENT_INCR('willtest')
SELECT * FROM willtest
DROP TABLE willtest

No, there isn't any guaranteed way (although you can certainly find out what the next value might be, another command might go and use it before you can make any use of it). The only guaranteed value you can retrieve is the previously inserted identity value through SCOPE_IDENTITY() (which will return the identity value last generated for the current scope).
It's questionable what purpose why one would need to know the value before (when using an automatically incremented seeded identity column).
If you need to know the value before, then I recommend generating the ids yourself. You can do this with an ids table keyed on the table name, or, if you have scalability concerns (and you are using transactions) you can have an id table for each table that needs an id which would have the id to be inserted (and subsequently incremented).
Or, you could use a GUID, and you would be able to easily generate these on the client side before sending it to your database.

This piece of sql will give you the next identity column value (there are probably many reasons not to repeat this snippet in production code)
declare #nextid int;
declare #previousid int;
begin tran
insert into dbo.TestTable (Col1) values ('11');
select #nextid = SCOPE_IDENTITY();
rollback tran
select #previousid = #nextid -1
DBCC CHECKIDENT('dbo.TestTable', RESEED, #previousid);
select #nextid
this stackoverflow question gives some extra information - sql-identity-autonumber-is-incremented-even-with-a-transaction-rollback

SELECT IDENT_CURRENT('mytable') + IDENT_INCR('mytable') FROM mytable

Since you seed from 1 and increment by 1 (IDENTITY(1,1)), I'm wondering if you can create a procedure where you can set a variable like "Select ##IDENTITY + 1" or something like that.

Use GUID columns for your primary keys. Unless you have billions of records and thousands of requests per second, you probably won't notice the performance difference. But unless you like spending far too much time dealing with stupid issues like this, you will notice the difference in your stress level and life expectancy.

Related

Reinsert primary key in the same record

I need to insert records into a production table. The problem is that one of the fields needs to be the same value as the primary key.
In the example below, the Insert query is dropping '99' into [AlsoMyID]. But that's just a placeholder. It needs to be whatever value is going into [MyID].
How do I write the Insert query so that the system will add the same PK value to both [MyID] and [AlsoMyID]?
Drop table #mylittletable
Create table #Mylittletable (
[MyID] int IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[AlsoMyID] int,
[ActualData] varchar(1))
Select * from #Mylittletable
Insert into #Mylittletable values (99,'x')
Select * from #Mylittletable
If you're interested in the background, the developer is using AlsoMyID as a linking field so any number of records can be linked together using the original primary key value. That said, I have no control over the table structure.
Firstly, you cannot specify the value for identity column unless you use set identity_insert on. so according to your requirement, you need to insert the same value to AlsoMyID as MyID.
You can work it out as flowing:
insert into Mylittletable
select ##IDENTITY+1,'1'
With this trigger on the table you can insert anything on the alsoMyID-column and that will be overwritten with what get's set in the myID-column.
create trigger tr_Mylittletable ON Mylittletable
AFTER INSERT AS
BEGIN
declare #ID int = (select MyID from inserted)
update Mylittletable set AlsoMyID = #ID where MyID = #ID
END
NOTE: This only works when making inserts of one line at a time!

SQL Server : multiple queries with Auto ID identity

I have a stored procedure that inserts some values to a specific table:
CREATE PROCEDURE AccessoiresAddOrder
#AccessoireID int,
#Qte int,
#ClientID int,
#Price Varchar(50),
#Totalprice Varchar(50),
#Date DateTime,
#ValueInt
AS
INSERT INTO [dbo].[Accessoires_Orders] ([AccessoireID], [Qte], [ClientID], [Price], [Totalprice],[Date])
VALUES (#AccessoireID, #Qte, #ClientID, #Price, #Totalprice, #Date)
INSERT INTO [dbo].[Accessoires_ordervalue] (Value)
VALUES (#Value)
The Acccessoires_Orders table has a column ID which is "auto identity". Is there any way to get that value during the procedure execution?
int ID = Max(Accessoires_Orders.ID) + 1
Why ?
Because this procedure can be executed by multiple users at the same exact time.
You can use either:
SELECT #MyNewID = SCOPE_IDENTITY()
SELECT #MyNewID = ##IDENTITY
SCOPE_IDENTITY() will always give you the value created by the query itself. ##IDENTITY can give you that value as well, but if a trigger executes after your insert, it will return the ID generated by the trigger instead.
Since you want the ID you created in the query, you'd be best off using SCOPE_IDENTITY().
You can use ##IDENTITY variable that returns you the last identity value returned within the current session - see: MSDN ##IDENTITY. However, if there are e.g. some triggers behind the table you can receive an identity value created by this trigger in a completely different table...
So generally recommended way is to rather use the SCOPE_IDENTITY() - see: MSDN SCOPE_IDENTITY()
After inserting a record to a able with an Identity column, the latest value inserted in the last row will be available in ##Identity variable.
You can select value of ##Identity and return is from your stored procedure. Following is a sample code for that.
INSERT INTO Production.Location (Name, CostRate, Availability, ModifiedDate)
VALUES ('Damaged Goods', 5, 2.5, GETDATE());
GO
SELECT ##IDENTITY AS 'Identity';
GO

Using ##identity or output when inserting into SQL Server view?

(forgive me - I'm new to both StackOverflow & SQL)
Tl;dr - When using ##identity (or any other option such as scope_identity or output variable), is it possible to also use a view? Here is an example of a stored procedure using ##identity:
--SNIP--
DECLARE #AID INT
DECLARE #BID INT
INSERT INTO dbo.A (oct1)
VALUES
(#oct1)
SELECT #AID = ##IDENTITY;
INSERT INTO dbo.B (duo1)
VALUES
(#duo2)
SELECT #BID = ##IDENTITY
INSERT INTO dbo.tblAB (AID, BID)
VALUES
(#AID, #BID)
GO
Longer:
When inserting into a table, you can capture the current value of the identity seed using ##identity. This is useful if you want to insert into table A and B, capture the identity value, then insert into table AB relating A to B. Obviously this is for purposes of data normalization.
Let's say you were to abstract the DB Schema with a few that performs inner joins on your tables to make the data easier to work with. How would you populate the cross reference tables properly in that case? Can it be done the same way, if so, how?
Avoid using ##IDENTITY or SCOPE_IDENTITY() if your system is using Parallel plans as there is a nasty bug. Please refer -
http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=328811
Better way to fetch the inserted Identity ID would be to use OUTPUT clause.
CREATE TABLE tblTest
(
Sno INT IDENTITY(1,1) NOT NULL,
FirstName VARCHAR(20)
)
DECLARE #pk TABLE (ID INT)
INSERT INTO tblTest(FirstName)
OUTPUT INSERTED.Sno INTO #pk
SELECT 'sample'
SELECT * FROM #pk
EDIT:
It would work with Views as well. Please see the sample below. Hope this is what you were looking for.
CREATE VIEW v1
AS
SELECT sno, firstname FROM tbltest
GO
DECLARE #pk TABLE (ID INT)
INSERT INTO v1(FirstName)
OUTPUT INSERTED.Sno INTO #pk
SELECT 'sample'
SELECT ID FROM #pk
##IDENTITY returns the last IDENTITY value produced on a connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value.
SCOPE_IDENTITY() returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value. SCOPE_IDENTITY(), like ##IDENTITY, will return the last identity value created in the current session, but it will also limit it to your current scope as well
Although the issue with either of these is fixed by microsoft , I would suggest you should go with "OUTPUT", and yes, it can be used with view as well

How to avoid "SQL Server automatically uses the new inserted value as the current identity value."

I'm using SQL Server 2008
as per microsoft, http://msdn.microsoft.com/en-us/library/ms188059.aspx
when I execute the following
set identity_insert on
//insert statements here
set identity_insert off
the identity of the column is set to the maximum value. Can I avoid this?
Consider the following scenario,
my table has 2 rows as follows
id, name comm
1, John, 232.43
2, Alex, 353.52
now using the above code, when I insert
10, Smith, 334.23
as per the above link, SQL Server automatically sets the identity to 10. So for newly inserted records (without using identity_insert on), id automatically starts with 11.
I want the identity value to be 3, after using identity_insert on/off
please help.
Here's a test table for this discussion
create table t4721736 ( id int identity primary key, name varchar(10), comm money )
insert t4721736 select 'John', 232.43 -- id=1
insert t4721736 select 'Alex', 353.52 -- id=2
-- check contents
select * from t4721736
-- do all this in a transaction
BEGIN TRAN
-- dummy insert
insert t4721736 select 'dummy', null
-- get what the id should be
declare #resetto bigint
set #resetto = scope_identity()
-- remove dummy record
delete t4721736 where id = #resetto
-- perform the insert(s)
set identity_insert t4721736 on;
insert t4721736(id,name,comm) select 10000000, 'Smith', 334.23;
set identity_insert t4721736 off;
-- reset the identity
set #resetto = #resetto - 1 -- it needs to be 1 prior
DBCC CHECKIDENT(t4721736, RESEED, #resetto)
COMMIT
Assuming you fully understand (I believe you do) that it will fail as soon as the range runs up to the records with the nominated IDs. SQL Server won't perform any auto-skip over IDs that already have records attached.
that will not be a problem, coz when i
insert using identity_insert on, value
of id will be greater than 10 million.
so there will not be any problem of
clashing
To see how this fails, shortcut the process by changing the "10000000" into "10" in the code above. Then, follow up with these:
-- inspect contents, shows records 1,2,10
select * from t4721736
-- next, insert 7 more records, bringing the id up to 9
insert t4721736 select 'U3', 0
insert t4721736 select 'U4', 0
insert t4721736 select 'U5', 0
insert t4721736 select 'U6', 0
insert t4721736 select 'U7', 0
insert t4721736 select 'U8', 0
insert t4721736 select 'U9', 0
Finally, try the next insert below
insert t4721736 select 'U10', 0
You can reset the seed value using DBCC CHECKIDENT:
DBCC CHECKIDENT ("MyTable", RESEED, 3);
GO
However, you have inserted a record Id of 10, so yes, the next one will indeed be 11.
It is documented on the command:
If the current identity value for a table is less than the maximum identity value stored in the identity column, it is reset using the maximum value in the identity column.
You can't have it both ways. Either have the lowest ID be the value of the base seed, or not.
If these rows you're inserting are special/magic rows (so they need specific IDs), have you considered making these rows have negative ID values? That way there's no conflict, and the IDENTITY value will not be reset by your adding them.
If it's some other reason why you need to insert these rows with vastly different ID values, perhaps you could expand your question to provide some info on that - we may be able to offer better solutions.
Another way to get around the "planted bug" dilemma is to create your own identity generator procedure and tracking table. The table includes a tablename and value that the next ID should be. This way you can reset it any value at any time. The procedure would include logic to check to see if the next generated key exists and if it does exist it will increment the key till it finds an ID that does not exist in the table and pass that back out to you. This would have to be implemented on all inserts to work correctly. Which is possible with a trigger. The downside is more processing overhead than using a negative number like Damien_The_Unbeliever suggests.

How can I reseed an identity column in a T-SQL table variable?

I have a T-SQL table variable (not a table) which has an auto incrementing identity column. I want to clear all data from this variable and reset the identity column value to 1. How can this be done?
If you're using a table variable, you can't do it. If it were a table, you could truncate it or use DBCC CHECKIDENT. But, if you have to use a table variable, you have to use something other than an identity column. Or, more accurately, use the identity column in your table variable but output using ROWNUMBER:
DECLARE #t table (pkint int IDENTITY(1,1), somevalue nvarchar(50))
INSERT INTO #t (somevalue) VALUES( 'one')
INSERT INTO #t (somevalue) VALUES('twp')
INSERT INTO #t (somevalue) VALUES('three')
SELECT row_number() OVER (ORDER BY pkint), somevalue FROM #t
DELETE FROM #t
INSERT INTO #t (somevalue) VALUES('four')
SELECT row_number() OVER (ORDER BY pkint), somevalue FROM #t
It's the best you can do with the table variable.
Truncating the table will dump ALL the data, and reset the identity seed.
Otherwise, you can use this call to reset the identity while retaining any of the data:
DBCC CHECKIDENT (yourtableName, reseed, #NewStartSeedValue)
I suggest you use two table variables. The #Table1 has an identity seed on the first column. #Table2 has the same first column but no identity seed on it.
As you loop through your process,
Insert into #Table2 from #Table1
then Delete From both Tables as your Process Loops.
On your first pass, the #Table2 will have a a sequential number in the first row starting at 1.
The second time through the loop your second table might have sequential numbers in the first column starting at say 1081. But if you select the minimum value to a variable
(Select #FixSeed = min(RowID) From #Table2)
Then you can update #Table2 to make RowID start at 1 as follows:
Update #Table2 Set RowID = RowID - #FixSeed +1
Hope this helps
declare #tb table (recid int,lineof int identity(1,1))
insert into #tb(recid)
select recid from tabledata
delete from #tb where lineof>(select min(lineof) from #tb)+#maxlimit
I did this when I wanted to use a TOP and a variable when using SQL 2000. Basically, you add in the records and then look at the minimum one. I had the same problem and noticed this thread. Deleting the table doesn't reset the seed although I imagine using GO should drop the table and variable to reset the seed.
#maxlimit in the query above was to get the top 900 of the query and since the table variable would have a different starting identity key, this would solve that issue.
Any subsequent query can subtract that derived procedure to make it insert as "1", etc.
If you need to truncate the table variable in each turn of a while loop, you can put the declare #myTbl (...) statement in the loop. This will recreate the table and reset the identity column on each turn of the loop. However, it has a heavy performance hit. I had fairly tight loop, and redeclaring the table variable relative to delete #myTbl was several times slower.
Dan

Resources