I have one table called customer_master that includes a column called cust_id with autoincrement set to 1.
When we try to insert records its working fine and inserted records like cust_id 1, 2, 3 and 4 are inserted, but when an error is generated in the insert command we do a transaction rollback, this means that cust_id 5 is not inserted, but when we are insert another record, cust_id generates 6. It skips cust_id 5.
I want to set it up so that if any error is generated in the insert command the identity is not incremented.
We are using c# and sql server 2005.
The reason SQL Server does this is for efficiency. If you need a sequence number without gaps you shouldn't be using identity you would need to implement your own scheme where concurrent transactions are blocked waiting for the next value just in case the initial transaction rolls back.
The second query here could be used for that purpose. But do you really need this? If it is purely for aesthetic purposes my advice is not to worry about it!
You can use DBCC CHECKIDENT to reseed the identity column after an insert failure.
DBCC CHECKIDENT ( table_name, NORESEED ) returns the current identity value and the current maximum value of the identity column.
DBCC CHECKIDENT ( table_name, RESEED, new_reseed_value ) sets the current identity value to the new_reseed_value.
Related
In SQL Server 2012, the following query is seeding the identity column myTable_id from 2 instead of 1. Why? myTable_id is also PK.
DELETE FROM myTable;
GO
SELECT * FROM myTable --0 rows are returned as expected
GO
DBCC CHECKIDENT(myTable, RESEED,1)
GO
INSERT INTO myTable(col1,col2,col3) SELECT FROM AnotherTable(col1,col2,col3)
GO
SELECT * FROM myTable --1005 rows are returned as expected, but identity value starts from 2
GO
Remark:
The data inserted is right, the only issue is that the newly inserted data starts from 2 instead of 1.
In the above sql code if I use DBCC CHECKIDENT(myTable, RESEED,0) the identity column correctly starts from 1.
Following is snapshot in SSMS for the myTable_id column:
From the docs:
The seed value is the value inserted into an identity column for the very first row loaded into the table. All subsequent rows contain the current identity value plus the increment value where current identity value is the last identity value generated for the table or view.
So if you seed from 10, the next value to be inserted will be 11.
There is nothing bad with the answer here but the confusion comes from Microsoft approach itself.
I think that:
DBCC CHECKIDENT(myTable, RESEED, 0)
Should have the same behavior everywhere:
on new created table,
after delete table records,
after truncating the table
Otherwise we need to check the table status before running this.
Works as expected see also
https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkident-transact-sql#examples
The value 1 means that the current identity will be at 1 and the next identity will start on 2
To get it starting on 1 you should do
DBCC CHECKIDENT(myTable, RESEED, 0)
This does the trick for me:
DBCC CHECKIDENT ([Table], RESEED, 0)
DBCC CHECKIDENT ([Table], RESEED)
I've already read the following answers about the impossibility to alter a column into identity once has been created as a regular int.
Adding an identity to an existing column
How to Alter a table for Identity Specification is identity SQL Server
How to alter column to identity(1,1)
But the thing is I have a table which has been migrated to a new one where the ID was not declared as identity from the beginning, because the old table which was created with an ID identity a long time ago has missing rows due to a purge of historical data. So as far as I know, if I add a new column as identity on my new table, it will automatically create the column sequentially and I need to preserve the IDs from the old table as-is because there is already data linked to these previous IDs.
How can I do transform my ID column from the new table as identity but not sequentially, but with the IDs from the old table?
You could try this approach:
Insert rows with old ID with SET IDENTITY_INSERT <new table> ON. This allows you to insert your own ID.
Reseed the Identity, setting it to the highest ID value +1 with DBCC CHECKIDENT ('<new table>', RESEED, <max ID + 1>). This will allow your Identity to increase from the highest ID and forward.
Something like this in code:
-- Disable auto increment
SET IDENTITY_INSERT <new table> ON
-- <INSERT STUFF HERE>
SET IDENTITY_INSERT <new table> OFF
-- Reseed Identity from max ID
DECLARE #maxval Int
SET #maxval = ISNULL(
(
SELECT
MAX(<identity column>) + 1
FROM <new table>
), 0)
DBCC CHECKIDENT ('<new table>', RESEED, #maxval)
EDIT: This approach requires your ID-column to be an Identity, of course.
If you don't have nulls in the field that you want to copy over from your previous version, you could first figure out what the largest ID is by just doing a max(Id) select. Then using SSMS go add your new field and when you set it as identity, just set the SEED value to something higher than what your current max is so you don't have collisions on new inserts.
I have a process where a temp table is used between a source file, CSV and the production table. The temp table has to match the CSV file columns, there is no PK in this data.
To find a set of rows before and after where the Azure Data Factory was failing, I imported over 2,000,000 rows into a temp table. The process stopped in Azure at 1,500,000 rows.
The error was that an integer or string would be truncated.
This line of code added a PK to the temp table and incremented it:
ALTER TABLE ##FLATFILETEMPBDI ADD ROWNUM INT IDENTITY
That would be the simplest solution to get a row number. I was then able to do this query to find the rows just before and after 1,500,000:
SELECT
ROWNUM
, PARTDESCRIPTION
, LEN(PARTDESCRIPTION) AS LENDESCR
, QUANTITY
, ONORDER
, PRICE
, MANUFACTURERPARTNUMBER
FROM ##FLATFILETEMPBDI
WHERE ROWNUM BETWEEN 1499990 AND 1500005
Works perfectly -- was not planning on it to be that easy, was surprised as anyone to see that the ALTER TABLE with IDENTITY worked to do the numbering for me.
I have a table in my db whose id is int auto increment. When I create a ndew entry everything goes well, the id is incremented by 1.
But today I saw a big hole. One of the records has id=56 and the next on has id=1055.
What could cause this. We didn't make backups on this db.
There are several things that can cause this
someone reseeded the table by running DBCC CHECKIDENT
someone inserted a bunch of rows and deleted them
A batch of rows was attempted to be inserted, the batch failed,
everything was rolled back, SQL Server does not reuse identity
values
Also keep in mind, if you delete all the rows with a DELETE statement, the seed is not reset back to 1, if you TRUNCATE the table then it is
Most probably there had been some errors on insert, or transaction rollbacks.
Identity value of a table is not affected by transaction. So for example, even if you insert a value into the table, and rollback the transaction, your indentity value will "stay incremented". Short example to illustrate:
create table _test (
id int identity (1,1)
,txt varchar(2)
)
insert into _test(txt)
select 'aa' as txt
insert into _test(txt)
select 'bb' as txt union all
select 'ccc'
GO
insert into _test(txt)
select 'dd' as txt
select * from _test
I am using SqlServer 2005 and I have a table in which I have an auto incrementing column but for some reason the auto increment field does not start with 1 but with some random number like 21,91. Why does that happen?
You either need to set the Seed for the column.... or if you had entered rows previously, you need to execute a TRUNCATE TABLE command on the table...
TRUNCATE TABLE XYZ
mssql is not using max(id) + 1 as identity like other databases. It is storing the last used id and is incrementing it.
You can reseed the identity:
DBCC CHECKIDENT ('tablex', RESEED, 1)
or truncate the table, this is also deleting all the data:
TRUNCATE TABLE tablex
You can of course combine the identity reseed with the last value:
DBCC CHECKIDENT ('tablex', RESEED, (SELECT max(id) + 1 FROM tablex))
But be aware of producing errors on reseeding the id due to conflicts, the auto increment id is unique!
I have various reasons for needing to implement, in addition to the identity column PK, a second, concurrency safe, auto-incrementing column in a SQL Server 2005 database. Being able to have more than one identity column would be ideal, but I'm looking at using a trigger to simulate this as close as possible to the metal.
I believe I have to use a serializable isolation level transaction in the trigger. Do I go about this like Ii would use such a transaction in a normal SQL query?
It is a non-negotiable requirement that the business meaning of the second incrementing column remain separated from the behind the scenes meaning of the first, PK, incrementing column.
To put things as simply as I can, if I create JobCards '0001', '0002', and '0003', then delete JobCards '0002' and '0003', the next Jobcard I create must have ID '0002', not '0004'.
Just an idea, if you have 2 "identity" columns, then surely they would be 'in sync' - if not exactly the same value, then would differ by a constant value. If so, then why not add the "second identity" column as a COMPUTED column, which offsets the primary identity? Or is my logic flawed here?
Edit : As per Martin's comment, note that your calc might need to be N * id + C, where N is the Increment and C the offset / delta - excuse my rusty maths.
For example:
ALTER TABLE MyTable ADD OtherIdentity AS Id * 2 + 1;
Edit
Note that for Sql 2012 and later, that you can now use an independent sequence to create two or more independently incrementing columns in the same table.
Note: OP has edited the original requirement to include reclaiming sequences (noting that identity columns in SQL do not reclaim used ID's once deleted).
I would disallow all the deletes from this table altogether. Instead of deleting, I would mark rows as available or inactive. Instead of inserting, I would first search if there are inactive rows, and reuse the one with the smallest ID if they exist. I would insert only if there are no available rows already in the table.
Of course, I would serialize all inserts and deletes with sp_getapplock.
You can use a trigger to disallow all deletes, it is simpler than filling gaps.
A solution to this issue from "Inside Microsoft SQL Server 2008: T-SQL Querying" is to create another table with a single row that holds the current max value.
CREATE TABLE dbo.Sequence(
val int
)
Then to allocate a range of sufficient size for your insert
CREATE PROC dbo.GetSequence
#val AS int OUTPUT,
#n as int =1
AS
UPDATE dbo.Sequence
SET #val = val = val + #n;
SET #val = #val - #n + 1;
This will block other concurrent attempts to increment the sequence until the first transaction commits.
For a non blocking solution that doesn't handle multi row inserts see my answer here.
This is probably a terrible idea, but it works in at least a limited use scenario
Just use a regular identity and reseed on deletes.
create table reseedtest (
a int identity(1,1) not null,
name varchar(100)
)
insert reseedtest values('erik'),('john'),('selina')
select * from reseedtest
go
CREATE TRIGGER TR_reseedtest_D ON reseedtest FOR DELETE
AS
BEGIN TRAN
DECLARE #a int
SET #a = (SELECT TOP 1 a FROM reseedtest WITH (TABLOCKX, HOLDLOCK))
--anyone know another way to lock a table besides doing something to it?
DBCC CHECKIDENT(reseedtest, reseed, 0)
DBCC CHECKIDENT(reseedtest, reseed)
COMMIT TRAN
GO
delete reseedtest where a >= 2
insert reseedtest values('katarina'),('david')
select * from reseedtest
drop table reseedtest
This won't work if you are deleting from the "middle of the stack" as it were, but it works fine for deletes from the incrementing end.
Reseeding once to 0 then again is just a trick to avoid having to calculate the correct reseed value.
if you never delete from the table, you could create a view with a materialized column that uses ROW_NUMBER().
ALSO, a SQL Server identity can get out of sync with a user generated one, depending on the use of rollback.