I hope the question is not too generic.
I have a table Person that has a PK Identity column Id.
Via C#, I insert new entries for Person and the Id get set to 1,2,3 for the 3 persons added.
Also via C#, I perform all deletions of the persons with Id=1,2,3 so that there's no Person in the Table anymore.
Afterwards, I run some change scripts (I can't post them as they are too long) also on Table Person.
I don't do any RESEED.
Now the fun:
If I call SELECT IDENT_CURRENT('Person') it shows 3 instead of 4.
If I do an insert of Person again, I get a Person with the Id 3 added instead of Id 4.
Any idea why and how this can happen?
EDIT
I think I found the explanation of my question:
While performing DB Changes via SQL Server Management Studio, The Designer creates
a temp table Tmp_Person and moves the data from Person inside there. Afterwards he performs a rename of Tmp_Person to Person. Since this is a new table the Index starts again from the beginning.
An IDENTITY property doesn't guarentee uniqueness. That's what a PRIMARY KEY or UNIQUE INDEX is for. This is covered in the documentation in the remarks section, along with other intended behaviour. CREATE TABLE (Transact-SQL) IDENTITY (Property) - Remarks:
The identity property on a column does not guarantee the following:
Uniqueness of the value - Uniqueness must be enforced by using a PRIMARY KEY or UNIQUE constraint or UNIQUE index.
Consecutive values within a transaction - A transaction inserting multiple rows is not guaranteed to get consecutive values for the rows
because other concurrent inserts might occur on the table. If values
must be consecutive then the transaction should use an exclusive lock
on the table or use the SERIALIZABLE isolation level.
Consecutive values after server restart or other failures -SQL Server might cache identity values for performance reasons and some of
the assigned values can be lost during a database failure or server
restart. This can result in gaps in the identity value upon insert. If
gaps are not acceptable then the application should use its own
mechanism to generate key values. Using a sequence generator with the
NOCACHE option can limit the gaps to transactions that are never
committed.
Reuse of values - For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a
particular insert statement fails or if the insert statement is rolled
back then the consumed identity values are lost and will not be
generated again. This can result in gaps when the subsequent identity
values are generated.
These restrictions are part of the design in order to improve
performance, and because they are acceptable in many common
situations. If you cannot use identity values because of these
restrictions, create a separate table holding a current value and
manage access to the table and number assignment with your
application.
Emphasis mine for this question.
Related
The idea
So, the title may seem somewhat vague but this is what I would like to have (in Microsoft SQL Server, recent version), to be used from an ASP.NET C# application:
A table with an ordinary primary key, defined as an "official" identity column
some other columns
An additional "logical identity" column
The additional "logical idendity" column should have the following properties
be of type integer
not strictly unique (multiple rows can have the same "locigal idendity")
mandatory
immutable (once set, it may never change). However DELETE of the row must be allowed.
When not provided at INSERT, set to a not yet used value
The last point is probably the hardest to achieve, so that's the question:
The question
How to enforce (preferably on the database level) that a mandatory value is always set to a yet unique value, when not provided by the INSERT script?
The thoughts
What I have considered yet:
Having a normal "identity" on that column is not possible because it's not unique among the existing values
Having a random value is not possible, because it must be unique for new values
Extending the =SaveChanges= Method would be problematic, because it would require to query the database in it
Maybe a database triggered function, but I would hope that there are easier solutions
The context
On some occations, especially when there will be an additional row with the same "logical idendity" insert, the application already defines the "loigcal idendity", and it should be used.
Currently, when the application sets a value as "logical ID" it will be among the existing values. Thus, I could force the database to accept only INSERTed values that at least exist once. This would help it when required to provide new, unique values.
However, if this is some sort of new item, the system should provide a new "locigal idendity" on the fly, while inserting. It must be sure, that no existing value is reused for this.
I will use Entity Framework (Version 6) as my ORM.
If the above requirements are not met, an exception should be thrown on the "Add"
If such a value would be changed, an exception should be thrown on the "Update"
One option is with a SEQUENCE value assigned with a DEFAULT constraint. The immutable requirement is the biggest challenge because SQL Server doesn't provide a declarative way to specify a read-only column so one needs a trigger implementation.
Below is example DDL. I don't know if this technique will pose challenges with EF.
CREATE SEQUENCE SQ_Example_Sequence
AS int START WITH 1 INCREMENT BY 1;
GO
CREATE TABLE dbo.Example(
IdentityColumn int NOT NULL IDENTITY
CONSTRAINT PK_Example PRIMARY KEY CLUSTERED
,SomeDataColumn int
,SequenceColumn int NOT NULL
CONSTRAINT DF_Example_SequenceColumn DEFAULT NEXT VALUE FOR SQ_Example_Sequence
);
GO
CREATE TRIGGER TR_Example_Update
ON dbo.Example FOR UPDATE
AS
IF EXISTS(SELECT 1
FROM inserted
JOIN deleted ON inserted.IdentityColumn = deleted.IdentityColumn
WHERE inserted.SequenceColumn <> deleted.SequenceColumn
)
BEGIN
THROW 50000, 'SequenceColumn value cannot be changed', 16;
END;
GO
I'm kind of new to SQL and databases and there's one thing that bothers me.
I'm using SQL Server for my ASP.NET MVC project and my database and its tables were auto-generated by Entity Framework using a code-first approach.
I have a table for book collections - just CollectionId and Name columns.
During development I've made many inserts and deletes in this table and right now it has 10 rows with Id's 1 to 10 (the initial entries). But when I add a new one it has the Id set to 37. Obviously in the past there were entries with Id up to 36, but there are now gone and these numbers seem to be free.
Then why a new entry does not have the Id set to 11? Is it a kind of limitation or maybe a security feature?
Thank you for answers.
This is default behavior when we define identity column. Whenever we perform delete operations there will be gaps in records for identity column.
Remarks from MSDN
If an identity column exists for a table with frequent deletions, gaps can occur between identity values. If this is
a concern, do not use the IDENTITY property. However, to ensure that
no gaps have been created or to fill an existing gap, evaluate the
existing identity values before explicitly entering one with SET
IDENTITY_INSERT ON.
IDENTITY
In addition to the other answer, it also has to do with performance of the server. The server typically cache's a group of ID's in memory to make assignment much faster, since the next number has to be stored on disk somewhere. So if the server allocates 100 numbers at a time, it only has to write to disk 1 out of every 100 usages (inserts) of the identity.
Trying to maintain gaps in the sequence would suck up a lot of time.
If you create a new table, insert a single row, kill the server and restart, you'll find the next insert will most likely contain a gap of whatever that number of cached values contains.
I am using sql server 2012, in my database I have set primaykey on userid also I have set the Identity specification Yes,Is Identity Yes,Identity Increment 1 and Identity Seed 1.
I just insert 5 users and userid is 1,2,3,4,5. I am sure after that I haven't did any insert and no other sp or trigger is using this table. this is just a new table. Now when I tried to insert 6th user it has inserted userid is 1001.
and for 7th 1002 and for 8th it inserted 2002 ,
why such jumped in userid?
Usually Gaps occur when:
1. records are deleted.
2. error has occurred when attempting to insert a new record (e.g. not-null constraint error).the identity value is helplessly skipped.
3. somebody has inserted/updated it with explicit value (e.g. identity_insert option).
4. incremental value is more than 1.
The identity property on a column does not guarantee the following:
Uniqueness of the value – Uniqueness must be enforced by using a PRIMARY KEY or UNIQUE constraint or UNIQUE index.
Consecutive values within a transaction – A transaction inserting multiple rows is not guaranteed to get consecutive values for the rows because other concurrent inserts might occur on the table. If values must be consecutive then the transaction should use an exclusive lock on the table or use the SERIALIZABLE isolation level.
Consecutive values after server restart or other failures –SQL Server might cache identity values for performance reasons and some of the assigned values can be lost during a database failure or server restart. This can result in gaps in the identity value upon insert. If gaps are not acceptable then the application should use a sequence generator with the NOCACHE option or use their own mechanism to generate key values.
Reuse of values – For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated.
Also,
If an identity column exists for a table with frequent deletions, gaps can occur between identity values. If this is a concern, do not use the IDENTITY property. However, to make sure that no gaps have been created or to fill an existing gap, evaluate the existing identity values before explicitly entering one with SET IDENTITY_INSERT ON.
Also, Check the Identity Column Properties & check the Identity Increment value. Its should be 1.
Open your table in design view
Now check that Identity Seed and Identity Increment values are correct. If not then you must correct them.
I have a table in MS SQL SERVER 2008 and I have set its primary key to increment automatically but if I delete any row from this table and insert some new rows in the table it starts from the next identity value which created gap in the identity value. My program requires all the identities or keys to be in sequence.
Like:
Assignment Table has total 16 rows with sequence identities(1-16) but if I delete a value at 16th position
Delete From Assignment Where assignment_id=16;
and after this operation when I insert a new row
Insert into Assignment(assignment_title)Values('myassignment');
Rather than assigning 16 as a primary key to this new value it assigns 17.
How can I solve this Problem ?
Renaming or re-numbering primary key values is not a good database management practice. I suggest you keep the primary key as is, and create a separate column index with the values you require to be re-numbered. Then simply create a trigger to run a routine that will re-number every row in the order you expect, obviously by seeking the "gaps" and entering them with values incremented from their previous value.
This is SQL Servers standard behaviour. If you deleted a row with ID=8 in your example, you would still have a gap.
All you could do, is write a function getSmallestDreeID in SQL Server, that you called for every insert and that would get you the smallest not assigned ID. But you would have to take great care of transactions and ACID.
The behavior you desire isn't possible without some post processing logic to renumber the rows.
Consider thus scenario:
Session 1 begins a transaction, inserts a row (id=16), but doesn't commit yet.
Session 2 begins a transaction, inserts a row (id=17) and commits.
Session1 rolls back.
Whether 16 will or will not exist in the table is decided after 17 is committed.
And you can't renumber these in a trigger, you'll get deadlocked.
What you probably need to do is to query the data adding a row number that is a sequential integer.
Gaps in identity values isn't a problem
well, i have recently faced the same problem: i need the ID values in an external C# application in order to retrieve files named exactly as the ID.
==> here is what i did to avoid the identity property, i entered id values manually because it was a small table, but if it is not in your case, use a SEQUENCE SQL Server 2014.
Use the statement UPDATE instead of delete to keep the id values in order.
I have use Identity on ID primary key.
And then I insert some data.
For example.
Data 1 -> Add Successful without error. ID 1
Data 2 -> Add Successful without error. ID 2
Data 3 -> Add Fail with error.
Data 4 -> Add Fail with error.
Data 5 -> Add Successful without error. ID 5
You can see that ID has jump from 2 to 5.
Why ?? How can solve this ??
Why would that be a problem ?
Normally, you'll use an identity in a primary key column. Then, this primary key is a surrogate key, which means that is has absolutely no business value / business meaning.
It is just an 'administrative' fact, which is necessary in order that the database can uniquely identify a record.
So, it doesn't matter what this value is; and it also doesn't matter that there are gaps. Why do you want them to be consecutive.
And, suppose that they are consecutive -that no gaps appear when an insert fails- what would you do when you delete a row, and insert one later on ? Would you fill in the gaps as well ?
this is by design, sql server first increments the counter and than tries to create row, if it fails transaction (there is implicit transactions always) is roll backed but auto increment value is not reused. this is by design and I would be very surprised to see that it can be avoided (eventually you could call some command and reset the value to current maximum). You can always use the trigger to generate this values, but this has performance implications, usually you should not care about the value of auto_increment its just an integer, you would have the same situation later in your application if th
If an insert failed, you can, for the next insert, use set identity_insert mytable on and calculate the next identity by hand, using max(myfield)+1. You might have concurrency issues though.
But this is a cludge. There's nothing wrong with gaps.
#Frederik answered most of it -- I would just add that you are mixing up primary keys and business keys. An invoice (or whatever) should be identified by an invoice number -- a business key which should have a UNIQUE column in the table. The primary key is here to identify a row in the table and should be used by the database (to join ..) and by DBAs only.
Exposing primary keys to business users will end up in trouble and the database will sooner or later lose referential integrity -- always does, people are creative.