Reseeding Identity Column to Original Value - sql

Is there any way to reseed an identity column to it's original value? Currently I have this IDENTITY(1,1) as my table definition. I've tried reseeding it using this method:
DBCC CHECKIDENT (Table1, reseed, 1)
But I noticed, that SQL actually reseeds it to current seed + the new seed value, so when I insert new rows it starts the auto-increment count at 2 instead of 1. I've also tried making the new reseed value 0, but then all the rows I insert after that end up being 1 and the column doesn't auto-increment anymore.
By the way, if it helps, I need this column to restart its auto-increment count at zero because I need for the count to restart every month. I have trigger after insert set up for this, but the reseeding is giving me some trouble.

The following statement:
DBCC CHECKIDENT('YourTableName', RESEED, 1);
Tells the database something akin to:
"Hey database! The highest identity value in the YourTableName table is #1, so the next inserted record should use identity value #2."
This explains is why your table starts with identity value 2 when you RESEED it using your statement.
More specifically Microsoft's help for DBCC CHECKIDENT() indicates:
Current identity value is set to the new_reseed_value. If no rows have been inserted to the table since it was created, or all rows have been removed by using the TRUNCATE TABLE statement, the first row inserted after you run DBCC CHECKIDENT uses new_reseed_value as the identity. Otherwise, the next row inserted uses new_reseed_value + the current increment value.
If the table is not empty, setting the identity value to a number less than the maximum value in the identity column can result in one of the following conditions:
If a PRIMARY KEY or UNIQUE constraint exists on the identity column, error message 2627 will be generated on later insert operations into the table because the generated identity value will conflict with existing values.
If a PRIMARY KEY or UNIQUE constraint does not exist, later insert operations will result in duplicate identity values.
It sounds like you want to change your table's initial starting seed value from 1 to 0. With this in mind, you need to modify the table's definition to use:
IDENTITY(0,1);
I hope this helps. Good luck.

Not really an answer, but too long for a comment and does cover the question.
I've also tried making the new reseed value 0, but then all the rows I insert after that end up being 1 and the column doesn't auto-increment anymore.
I can't reproduce this behaviour:
IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
DROP TABLE dbo.T;
CREATE TABLE dbo.T(ID INT IDENTITY(1,1) NOT NULL);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 1
DELETE dbo.T;
DBCC CHECKIDENT ('dbo.T', RESEED, 0);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 1, I.E. RESEED TO 0 HAS WORKED FINE
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 2, SHOWS INCREMENT IS WORKING AS EXPECTED
IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
DROP TABLE dbo.T;
It is worth noting however that for an empty table the identity will start at 0 if you use 0 as the RESEED value, i.e.
TRUNCATE TABLE dbo.T;
DBCC CHECKIDENT ('dbo.T', RESEED, 0);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
Will output 0 as the ID inserted.

I believe the only way to do this would be to select all your data into a 2nd table.
Then you can issue the command truncate table Table1.
This will reset the identity column.
Then you re-insert all your data back from temporary holding into the main table (without inserting the old identity).
Example...
NB: run each code block one at a time, checking counts along the way, don't run the whole thing -- and I've left the "dangerous" bits commented out just in case
/** select all columns but NOT the id into a temp location (dont use a # table) **/
SELECT col1, col2, col3, col4, col5
INTO tmpTable
FROM Table1
/** before truncation, double check counts in both match!! **/
-- SELECT COUNT(2) FROM tmpTable
-- SELECT COUNT(2) from Table1
-- TRUNCATE TABLE Table1
INSERT INTO Table1
SELECT col1, col2, col3, col4, col5
FROM tmpTable
/** before you drop temp table, make sure Table1 is ok! **/
-- SELECT COUNT(2) FROM tmpTable
-- SELECT COUNT(2) from Table1
-- DROP TABLE tmpTable
Here is a quick test to demonstrate -- I insert 8 rows, delete one, and re-insert. They are correctly re-numbered.
IF OBJECT_ID('DBO.TEST') IS NOT NULL DROP TABLE TEST
IF OBJECT_ID('DBO.TMP') IS NOT NULL DROP TABLE TMP
CREATE TABLE TEST ([ID] INT IDENTITY(1,1), [SOMESTRING] NVARCHAR(50))
INSERT INTO TEST([SOMESTRING])
SELECT 'HI' UNION
SELECT 'THESE' UNION
SELECT 'ARE' UNION
SELECT 'SOME' UNION
SELECT 'STRINGS' UNION
SELECT 'JUST' UNION
SELECT 'TO' UNION
SELECT 'DEOMONSTRATE'
SELECT *
FROM TEST
DELETE FROM TEST
WHERE SOMESTRING='SOME'
SELECT SOMESTRING
INTO TMP
FROM TEST
TRUNCATE TABLE TEST
INSERT INTO TEST
SELECT SOMESTRING
FROM TMP
SELECT * FROM TEST

Related

Odd SQL Server 2012 IDENTITY issue

I've never seen this happen before, very odd.
I have a local SQL Server 2012 Express database that I'm developing against. Running a simple suite of tests using the TestDrive plugin and accessing the database with EF v5.
I just ran a test that inserts a record into the database. I had 9 rows in the table going from id 1-9. The next insert and the ID jumped by exactly 10000 !!!!
The Id column goes:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10009
I know failed inserts also increment the ID but I can guarantee that 10,000 didn't fail to insert in the 5 seconds between test runs ...
The table structure is really simple, a bunch of columns and one auto incrementing, identity column of type bigint (long), no SPs, triggers or any other programmatic content.
[Id] [bigint] IDENTITY(1,1) NOT NULL,
Very confusing, has anyone else seen this happening?
This blog post has some additional details. It looks like in 2012, identity is implemented as a sequence. And by default, a sequence has a cache. If the cache is lost you lose the sequence values in the cache.
The proposed solution is to create a sequence with no cache:
CREATE SEQUENCE TEST_Sequence
AS INT
START WITH 1
INCREMENT BY 1
NO CACHE
As far as I can see, the sequence behind an identity column is invisible. You can't change it's properties to disable caching.
To use this with Entity Framework, you could set the primary key's StoredGeneratedPattern to Computed. Then you could generate the identity server-side in an instead of insert trigger:
if exists (select * from sys.sequences where name = 'Sequence1')
drop sequence Sequence1
if exists (select * from sys.tables where name = 'Table1')
drop table Table1
if exists (select * from sys.triggers where name = 'Trigger1')
drop trigger Trigger1
go
create sequence Sequence1
as int
start with 1
increment by 1
no cache
go
create table Table1
(
id int primary key,
col1 varchar(50)
)
go
create trigger Trigger1
on Table1
instead of insert
as
insert Table1
(ID, col1)
select next value for Sequence1
, col1
from inserted
go
insert Table1 (col1) values ('row1');
insert Table1 (col1) values ('row2');
insert Table1 (col1) values ('row3');
select *
from Table1
If you find a better solution, let me know :)
If you will call "Checkpoint" command after each insert query, it will solve your problem.
For more information, please read out Checkpoint in SQL Server

Insert into a temporary table and update another table in one SQL query (Oracle)

Here's what I'm trying to do:
1) Insert into a temp table some values from an original table
INSERT INTO temp_table SELECT id FROM original WHERE status='t'
2) Update the original table
UPDATE original SET valid='t' WHERE status='t'
3) Select based on a join between the two tables
SELECT * FROM original WHERE temp_table.id = original.id
Is there a way to combine steps 1 and 2?
You can combine the steps by doing the update in PL/SQL and using the RETURNING clause to get the updated ids into a PL/SQL table.
EDIT:
If you still need to do the final query, you can still use this method to insert into the temp_table; although depending on what that last query is for, there may be other ways of achieving what you want. To illustrate:
DECLARE
id_table_t IS TABLE OF original.id%TYPE INDEX BY PLS_INTEGER;
id_table id_table_t;
BEGIN
UPDATE original SET valid='t' WHERE status='t'
RETURNING id INTO id_table;
FORALL i IN 1..id_table.COUNT
INSERT INTO temp_table
VALUES (id_table(i));
END;
/
SELECT * FROM original WHERE temp_table.id = original.id;
No, DML statements can not be mixed.
There's a MERGE statement, but it's only for operations on a single table.
Maybe create a TRIGGER wich fires after inserting into a temp_table and updates the original
Create a cursor holding the values from insert and then loop through the cursor updating the table. No need to create temp table in the first place.
You can combine steps 1 and 2 using a MERGE statement and DML error logging. Select twice as many rows, update half of them, and force the other half to fail and then be inserted into an error log that you can use as your temporary table.
The solution below assumes that you have a primary key constraint on ID, but there are other ways you could force a failure.
Although I think this is pretty cool, I would recommend you not use it. It looks very weird, has some strange issues (the inserts into TEMP_TABLE are auto-committed), and is probably very slow.
--Create ORIGINAL table for testing.
--Primary key will be intentionally violated later.
create table original (id number, status varchar2(10), valid varchar2(10)
,primary key (id));
--Create TEMP_TABLE as error log. There will be some extra columns generated.
begin
dbms_errlog.create_error_log(dml_table_name => 'ORIGINAL'
,err_log_table_name => 'TEMP_TABLE');
end;
/
--Test data
insert into original values(1, 't', null);
insert into original values(2, 't', null);
insert into original values(3, 's', null);
commit;
--Update rows in ORIGINAL and also insert those updated rows to TEMP_TABLE.
merge into original original1
using
(
--Duplicate the rows. Only choose rows with the relevant status.
select id, status, valid, rownumber
from original
cross join
(select 1 rownumber from dual union all select 2 rownumber from dual)
where status = 't'
) original2
on (original1.id = original2.id and original2.rownumber = 1)
--Only math half the rows, those with rownumber = 1.
when matched then update set valid = 't'
--The other half will be inserted. Inserting ID causes a PK error and will
--insert the data into the error table, TEMP_TABLE.
when not matched then insert(original1.id, original1.status, original1.valid)
values(original2.id, original2.status, original2.valid)
log errors into temp_table reject limit 999999999;
--Expected: ORIGINAL rows 1 and 2 have VALID = 't'.
--TEMP_TABLE has the two original values for ID 1 and 2.
select * from original;
select * from temp_table;

It seems SELECT MAX([ColumnName]) returns value from deleted records if [ColumnName] is auto increment int type

In SQL Server or maybe other databases, if a column is auto increment int type, the table can remember the highest value even if the record has been deleted.Let's say you have a table whose with some records previously deleted, and then you use SELECT MAX([ColumnName]), it might return a value associated with a deleted record. Has anyone seen scenarios like this?
This will not happen with SELECT MAX (as can be seen from the below script) in Sql Server.
CREATE TABLE TEST_TABLE (
ID INT IDENTITY (1,1),
Val INT
)
INSERT INTO TEST_TABLE (Val) SELECT 1
INSERT INTO TEST_TABLE (Val) SELECT 2
INSERT INTO TEST_TABLE (Val) SELECT 3
INSERT INTO TEST_TABLE (Val) SELECT 4
INSERT INTO TEST_TABLE (Val) SELECT 5
DELETE FROM TEST_TABLE WHERE Val >= 3
SELECT MAX(ID) MaxID
FROM TEST_TABLE
DROP TABLE TEST_TABLE
Output
MaxID
2
If you were looking for the Tables Identity Value, see #AdaTheDev answer.
Yes, that's by design. Used IDENTITY values are not re-assigned when a record is deleted.
To get the last identity value assigned, you'd need to use IDENT_CURRENT, which (quote)
Returns the last identity value generated for a specified table or view. The last identity value generated can be for any session and any scope.
e.g.
SELECT IDENT_CURRENT('TableName')
I'd say that if you're taking the MAX() on an identity/auto-increment column, then you're doing something wrong.
As #astander says, it's not behaviour I've observed under SQL Server.
You should treat such identifiers as opaque - that they happen to be structured as an integer is a mere convenience to you, with well known storage requirements, clustering behaviours, etc.
It is dev (not sysadmin's or DBA's) board - don't do this in production!
To check what is the next ID:
DBCC CHECKIDENT ('YourTableName', NORESEED)
Note that your next ID will be #MaxID+1:
DECLARE #MaxID INT;
SELECT #MaxID = COUNT(ID) FROM YourTableName;
DBCC CHECKIDENT('YourTableName', RESEED, #MaxID);

Inserting 100,000 rows at a time .. error identity column violation

I'm trying to insert row by row in a table about 100k records.. I get this error after some 140 or so..
Violation of PRIMARY KEY constraint
'PK_table1'. Cannot insert duplicate
key in object 'table1'. The statement
has been terminated.
In this case the primary key is an IDENTITY column. I do not include that column in my INSERT statements.
I ran DBCC CHECKIDENT (table1,noreseed)
The current identity value and the current column value are NOT the same.
If I run the same command in 5 min, they become the same.
I cannot figure out what the problem is. Any help is greatly appreciated.
If the destination table is not empty then you want to reseed the identity column to the next highest existing value like so:
Declare #Max bigint
Set #Max = ( Select Max(IdCol) From TableA ) + 1
DBCC CHECKIDENT( TableA, RESEED, #Max )
You can use bcp command for this work. You can specify that identity being checked or not.

how to change identity increment of column

Following statement can reset seed
DBCC CHECKIDENT ('TableName', RESEED, 1)
but this time I have to change its increment .
Or you can use Sql Server Management Studio:
Using this approach will most likely recreate the table.
Hope this helps
ALTER TABLE MyCustomers
ALTER COLUMN CustId IDENTITY (200, 2)
Code from ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/ssmprog3/html/5719d3e4-14db-4073-bed7-d08f39416a39.htm
We can't update a column's identity to increment by 2 on each entry. The easiest method is to create another table with IDENTITY(1,2) and move the data to that table before dropping the actual table. Please go through the script below.
Let TableA is our actual table.
CREATE TABLE TableB(col1 INT IDENTITY (1,2) NOT NULL, col2 VARCHAR(10) NULL);
INSERT INTO TableB SELECT col2 FROM TableA;
DROP TABLE TableA;
sp_rename TableB, TableA;
You can reset the auto increment to a higher value by
Set IDENTITY_INSERT off
Running an insert statement with ID set to where you want to continue from
Set IDENTITY_INSERT on
Delete the row created (assuming it was only done to increment seed)
For example;
Table 'People' with last ID = 10 can be set to continue from 400,000 by;
SET IDENTITY_INSERT AccessToken off
insert into People(Id, name) values (400000,'Bob')
SET IDENTITY_INSERT AccessToken on
Delete from People where ID = 400000