Primary Key Duplication Exception - c#

I am working on a project in C# using sql server as a data base. So the problem is there is a algo in my project which returns a single value every time; which is saved to the database (as my project requirement). If the algo repeats a value, that will also save to the database which is not required (duplication) and cause some problems. I need help to overcome the problem that a unique value saves only once when it occurs; no repetition in database. I tried to make that column a primary key, but then I found primary key violation exception.

Just use an EXISTS query either in-line or in a stored procedure
Your query procedure can check if the row already exists
Edit: Aha sorry forgot the NOT, that would be stupid :D
IF NOT EXISTS(SELECT UniqueValue FROM UniqueValuesTable WHERE UniqueValue = #NewValue)
INSERT INTO UniqueValuesTable VALUES (#NewValue)
Edit: here is a SQL fiddle to show it working
http://sqlfiddle.com/#!3/b87f9/3
As dems pointed out, the operation isn't atomic, so in a multi session situation there could be a PK violation still
Alternative is:
INSERT INTO UniqueValuesTable SELECT #NewValue WHERE NOT EXISTS (SELECT UniqueValue FROM UniqueValuesTable WHERE UniqueValue = #NewValue)

Related

how to use SCOPE_IDENTITY() not in insert

I want to get new id(Identity) before insert it. so, use this code:
select SCOPE_IDENTITY() AS NewId from tblName
but is get this:
1- Null
2- Null
COMPUTED COLUMN VERSION
You'll have to do this on the sql server to add the column.
alter table TableName add Code as (name + cast(id as varchar(200)))
Now your result set will always have Code as the name + id value, nice because this column will remain updated with that expression even if the field are changed (such as name).
Entity Framework Option (Less ideal)
You mentioned you are using Entity Framework. You need to concatenate the ID on a field within the same record during insert. There is no capacity in SQL (outside of Triggers) or Entity Framework to do what you are wanting in one step.
You need to do something like this:
var obj = new Thing{ field1= "some value", field2 = ""};
context.ThingTable.Add(obj);
context.SaveChanges();
obj.field2 = "bb" + obj.id; //after the first SaveChanges is when your id field would be populated
context.SaveChanges();
ORIGINAL Answer:
If you really must show this value to the user then the safe way to do it would be something like this:
begin tran
insert into test(test) values('this is something')
declare #pk int = scope_identity()
print #pk
You can now return the value in #pk and let the user determine if its acceptable. If it is then issue a COMMIT else issue the ROLLBACK command.
This however is not a very good design and I would think a misuse of the how identity values are generated. Also you should know if you perform a rollback, the ID that would of been used is lost and wont' be used again.
This is too verbose for a comment.
Consider how flawed this concept really is. The identity property is a running tally of the number of attempted inserts. You are wanting to return to the user the identity of a row that does not yet exist. Consider what would happen if you have values in the insert that cause it too fail. You already told the user what the identity would be but the insert failed so that identity has already been consumed. You should report to the user the value when the row actually exists, which is after the insert.
I can't understand why you want to show that identity to user before insert, I believe (as #SeanLange said) that is not custom and not useful, but if you insist I think you can do some infirm ways. One of them is
1) Insert new row then get ID with SCOPE_IDENTITY() and show to user
2) Then if you want to cancel operation delete the row and reset
identity (if necessary) with DBCC CHECKIDENT('[Table Name]', RESEED,
[Identity Seed]) method
Other way is not using the Identity column and manage id column by yourself and it must be clear this approach can't be work in concurrency scenarios.
I think perhaps you're confusing the SQL identity with a ORACLE sequence.
They work completely different.
With the ORACLE sequence you'll get the sequence before you insert the record.
With a SQL Identity, the last identity generated AFTER the insert in available via the SCOPE_IDENTITY() function.
If you really need to show the ID to the user before the insert, your best bet is to keep a counter in a separate table, and read the current value, and increment that by one. As long as "gaps" in the numbers aren't a problem.

Query on Identity Column on MS SQL DB

I have created table in MS SQL 2008 with one identity column(Start Value - 1 and Increment is also 1) and 4 other columns. I am accessing this DB from C# ASP.NET. Used to push data only for the non identity column. Identity column will auto increment itself.
As of now i am manually querying the column value with the remaining for columns. But I am facing problem if all the other four column values are equal i am not getting the exact value which i am looking for
Now my query is, Is there any why in C# where I can get the value of the newly created identity column whenever new record is created.
Thanks.
You can use
SCOPE_IDENTITY()
Which will returns the primary key value of the recently inserted row
The answer to your question actually lies in SQL Server. You can run:
SELECT ##identity
after your insert to get the last inserted row identity.
http://technet.microsoft.com/en-us/library/aa933167(v=sql.80).aspx
EDIT BASED ON COMMENTS:
Consider using SCOPE_IDENTITY() as referenced here:
http://technet.microsoft.com/en-us/library/aa259185(v=sql.80).aspx
In SQL terms you can output the records back if you wish it. But how you might apply this to C# is up to you. Example:
INSERT INTO TABLE_A (SOMETHING, SOMETHINGELSE, RANDOMVAL3)
OUTPUT inserted.A_ID, inserted.SOMETHING, inserted.SOMETHINGELSE, inserted.RANDOMVAL3
SELECT 'ASD','DOSD', 123
But unless you're using merge, you can't use OUTPUT to print out any values from joining tables from an INSERT. But that's another matter entirely, I think.
Also, it's hardly good practice to bounce this data between the application and the DB all the time, so I'd look to alternatives if possible.

Primary key violation error in sql server 2008

I have created two threads in C# and I am calling two separate functions in parallel. Both functions read the last ID from XYZ table and insert new record with value ID+1. Here ID column is the primary key. When I execute the both functions I am getting primary key violation error. Both function having the below query:
insert into XYZ values((SELECT max(ID)+1 from XYZ),'Name')
Seems like both functions are reading the value at a time and trying to insert with the same value.
How can I solve this problem.. ?
Let the database handle selecting the ID for you. It's obvious from your code above that what you really want is an auto-incrementing integer ID column, which the database can definitely handle doing for you. So set up your table properly and instead of your current insert statement, do this:
insert into XYZ values('Name')
If your database table is already set up I believe you can issue a statement similar to:
alter table your_table modify column you_table_id int(size) auto_increment
Finally, if none of these solutions are adequate for whatever reason (including, as you indicated in the comments section, inability to edit the table schema) then you can do as one of the other users suggested in the comments and create a synchronized method to find the next ID. You would basically just create a static method that returns an int, issue your select id statement in that static method, and use the returned result to insert your next record into the table. Since this method would not guarantee a successful insert (due to external applications ability to also insert into the same table) you would also have to catch Exceptions and retry on failure).
Set ID column to be "Identity" column. Then, you can execute your queries as:
insert into XYZ values('Name')
I think that you can't use ALTER TABLE to change column to be Identity after column is created. Use Managament Studio to set this column to be Identity. If your table has many rows, this can be a long running process, because it will actually copy your data to a new table (will perform table re-creation).
Most likely that option is disabled in your Managament Studio. In order to enable it open Tools->Options->Designers and uncheck option "Prevent saving changes that require table re-creation"...depending on your table size, you will probably have to set timeout, too. Your table will be locked during that time.
A solution for such problems is to have generate the ID using some kind of a sequence.
For example, in SQL Server you can create a sequence using the command below:
CREATE SEQUENCE Test.CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
Then in C#, you can retrieve the next value out of Test and assign it to the ID before inserting it.
It sounds like you want a higher transaction isolation level or more restrictive locking.
I don't use these features too often, so hopefully somebody will suggest an edit if I'm wrong, but you want one of these:
-- specify the strictest isolation level
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
insert into XYZ values((SELECT max(ID)+1 from XYZ),'Name')
or
-- make locks exclusive so other transactions cannot access the same rows
insert into XYZ values((SELECT max(ID)+1 from XYZ WITH (XLOCK)),'Name')

Get latest identity, such as ID, from an empty table SQL

I have noticed that SQL keeps track of the latest identity (field it automatically increments each time a new record is created). I want to retrieve the latest identity from the table using C#. Unfortunately most of the time the table is empty (records are written and removed after a while, so often the table is empty).
Is this possible to do this within the bounds of the C# SQL API or do I have to create a stored procedure to retrieve this?
To better explain. If the row above was removed, the next ID number for the next record would be 32. I want to retrieve 32 before the record is written in, in the situation where the table is empty.
SELECT IDENT_CURRENT('table_name')+1;
IDENT_CURRENT returns the last identity value generated for a specific
table in any session and any scope.
http://msdn.microsoft.com/en-us/library/ms175098.aspx
However, although this shows what the next ID will be, this doesn't always mean it will be the next ID entered by yourself. Someone else could INSERT a new record before you.
In short, there is no way of returning the value of what you will next be inserting.
Just in case the increment is not the regular "1":
SELECT IDENT_CURRENT('mytable') + IDENT_INCR('mytable')
Of course, all these rely on the identity column having been populated consistently, ie no messing with manual inserts or reseeding. And Curt has mentioned the possible concurrency issue if you're going to use this ID for your insert directly.
That would be the following query
DBCC CHECKIDENT ('[TableName]', NORESEED )

Prevent failed Insert from claiming next Identity value

When an INSERT fails into a table with an auto incremented identity field, the identity is still incremented, thus producing gaps in the identity sequence. Is there a way to avoid this?
The only way to do this is to reseed the table or build your own identity generator
example
CREATE TABLE test(id INT IDENTITY, bla INT)
INSERT test VALUES(1)
INSERT test VALUES('b') --fails
DBCC CHECKIDENT(test,RESEED,1) --RESEED table
INSERT test VALUES(1)
SELECT * FROM test
DROP TABLE test
On a busy table you might get inserts after that and the reseed won't be correct anymore
But why do you need this? Who cares if there are gaps
Random idea from left field: Identify what's actually causing the failed inserts and validate against that, while of course realizing that due to the validations and the inserts not being atomic, you may still get failures (for things such as duplicates in unique fields).
It seems the identity gap is the symptom, the failure is the illness. Treat the illness.
Generally you want the key to have no meaning behind it other than identifying the record. That means if there are gaps in the numbering, it should not matter. If you are inserting a record into another related table, you can always use SCOPE_IDENTITY() in your stored proc/trigger, etc.

Categories

Resources