Setup
I have a uniqueidentifier column that's set as the primary key with a Default Value or Binding of (newsequentialid()).
What Works
In my MVC project, I can use things like [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] and Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); to allow SQL Server to take over and use the specified Default Value or Binding to automatically generate a Sequential Guid, and this works fine.
What Doesn't Work
However, if I write code outside of my MVC project, specifically, in LINQPad, an empty Guid, e.g., 00000000-0000-0000-0000-000000000000, is always inserted:
Resources.InsertOnSubmit(new Resource());
SubmitChanges();
I understand that I can write something like this to insert a randomized Guid value: new Resource() { Id = Guid.NewGuid() }, but I need a Sequential Guid.
Why I'm Confused
I was under the impression that when a null or empty value was passed to a SQL Server column, the Default Value or Binding, if specified, would automatically be used.
Question #1:
Why is the Default Value or Binding being ignored when using LINQPad?
Question #2:
How can I make SQL Server use the specified Default Value or Binding of (newsequentialid()) when I don't have access to the MVC attributes noted above?
With regards to your second question, SQL Server will ignore the default binding on your column if NULL is explicitly specified in the insert statement. Similarly, if your MVC project is generating a default Guidof 00000000-0000-0000-0000-000000000000 then SQL Server will simply insert the value into your table.
One option is to simply generate your own sequential GUID from your MVC project. There are plenty of example of sequential GUID generators in C#. I personally like Alex Siepman solution for sequential GUID you can find it at the following location:
http://www.siepman.nl/blog/post/2015/06/20/SequentialGuid-Comb-Sql-Server-With-Creation-Date-Time-.aspx
Alternatively, you can create INSTEAD OF INSERT trigger on your target table to correctly handle the insert in the manner you want. In your case you want to omit the ID being passed to the table and let SQL Server generate the ID for you. The trigger will look as follow:
CREATE TRIGGER AutoGuid_Trigger ON [dbo].[YourTableName]
INSTEAD OF INSERT AS
BEGIN
INSERT INTO [dbo].[YourTableName]
(
-- Exclude ID from column list, SQL Server will generate the ID
[YourCol1]
,[YourCol2]
,[YourCol3]
,...
)
SELECT [YourCol1]
,[YourCol2]
,[YourCol3]
,...
FROM inserted
END
Related
I am currently trying to insert a DataRow into an Oracle SQL Database. The entry gets inserted into the Database but always gets a new ID. I am providing an ID in the Insert Command but it always uses an auto increment and doesn't let me insert my ID.
What can I do?
There are 2 possible solutions to your problem.
Set column as NOT to be "Identity" column. This will take care of
your issue without any hassle.
If you must keep that column as "Identity" column, set "Generated"
property to "By Default on Null". This will ensure that when you are
NOT passing any value for ID, the system generates next sequence
number automatically.
For solution 2:
DDL Statement: ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY INCREMENT BY 1
Table Creation in SQL Developer:
I created mvc 4 application that can upload data into database using sql bulk copy upload method.
Once I select excel file, it can upload data into the system.
this is working fine, now I want to add default value if excel field is null
this is how it extract column values of excels
"s1.Gender, " +
"s1.Country_of_Birth, " +
"s1.Date_of_Birth, " +
I want to add default value from code level , I already handle this using database level.
for example to add default value for "date of birth" I added following constraint
ALTER TABLE [dbo].[tbl_HEI_student] ADD CONSTRAINT [DF_tbl_HEI_student_Date_of_Birth] DEFAULT (getdate()) FOR [Date_of_Birth]
But when I upload excel file into db ,SQL bulk copy upload method is ignoring adding that default value database .
How can I add default value from controller method
Your problem must be that the column allows NULLS.
In case your column allows NULL and you provide a NULL value on the insert the NULL WILL be inserted. If no value is provided for the column the DEFAULT will be considered.
Setup your column to NOT NULL.
ALTER TABLE [dbo].[tbl_HEI_student] ALTER COLUMN [Date_of_Birth] DATE NOT NULL
Here is the Default Definition.
EDIT:
As you have noticed, you can't BulkInsert the NULLS to that columns and make the DEFAULT value prevail on SqlServer side, and the NOT NULL i suggested will enforce that either no value is supplied or something other than NULL.
I can see a few options.
Either deal with them .NET side by setting the value to your default (you will have to show you .NET code if you want help with that)
doing one bulk for rows with value and another for rows without and removing the mapping for that column, the default constraint will take action
Setting the row back to allow NULLS, do the bulk as you were doing, and doing an update after the bulk to set the NULLS to your default value.
I know BULK INSERT and BCP both have a qualifier as shown here to deal with the NULL values. The same option is available in the .NET SqlBulkCopy options.
Here is another question related to your problem.
Keep in mind your database schema should help you enforce your business rules and keep your data clean. If the date should never be NULL, keep the column as NOT NULL and do your solution accordingly.
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.
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')
My SQL table contains an Id column which is of datatype uniqueidentifier. As per advice found on SO, I've set it to have a default value of (newid()) .
Using Entity Framework 4.2 with code first, I've then mapped my Guid property to the relevant field in SQL:
[Key]
public Guid Id { get; set; }
However, whenever I try to insert an entity I receive the following exception:
The type of one of the primary key values did not match the type defined in the entity.
The argument types 'Edm.Guid' and 'Edm.String' are incompatible for this operation. Near WHERE predicate, line 1, column 61.
The only solution I can find on both here and Google is to add [DatabaseGenerated(DatabaseGeneratedOption.Identity)] as a data annotation on my Id. This doesn't change a thing - so what else could be causing this error?
Thanks in advance.
have you try set identify StoreGeneratedPattern ?
you could do it in OnModelCreate Method.
Model.Entity<Foo>().Property(o => o.Id).HasDatabaseGenerationOption(DatabaseGeneratedOption.Identity);
Using a Guid as a primary key is ill-advised if you need performance and have an index over it. The Guids are being generated with randomized values, so every time you insert a new item it will cause more work for SQL to update the index than if you used a bigint (it has to insert into different parts of the index each time instead of always appending to the end). If you need a unique identifier (such as to link items across systems that don't share the same database) then simply add it as an additional column on the table (and if you are paranoid and willing to take a perf hit on insert/update, you could additionally add a unique constraint over it).