Alter column identity with Microsoft.SqlServer.Smo - c#

I've trying to change a column IDENTITY using Microsoft.SqlServer.Smo from a table with no dependencies and with all the data loaded previously (from another DB) but i get an error like this "Modifying the Identity property of the Column object is not allowed. You must drop and recreate the object with the desired property". The thing is that i tried to do this with Management Studio and it has no problem with it. Do you have any suggestions?. Thanks in Advance
This is the code:
foreach (Column source in sourcetable.Columns)
{
try
{
if(source.Identity)
{
Column column = copiedtable.Columns[source.Name];
// column.Computed = source.Computed;
// column.ComputedText = source.ComputedText;
column.Identity = source.Identity;
column.IdentityIncrement = source.IdentityIncrement;
column.IdentitySeed = source.IdentitySeed;
column.Alter();
}
}
catch { }
}

Try doing it in SSMS again and choose to script the action out instead of applying it directly. You'll find that it creates a temporary table with the identity property set to true (and everything else the same), copies your data from the live table into the temp table, drops the live table, and renames the temp table to be the live table. You'll need to do something similar with SMO.
Copying the table is easy enough: iterate over the columns, indexes, foreign keys, etc and create your new table that way (taking care to set the identity property correctly properly before you call Create()). For moving the data, take a look at the Transfer class. Once that's done, it's a drop and rename (or a rename and rename if you want to be safe).
I'm a little surprised that SMO doesn't do this somehow under the covers (since SSMS uses SMO under the covers). If I find something else that makes it do this automatically, I'll let you know.

Are you trying to update an existing record, or add a new record? If you're adding a new record, then do an insert. If you're updating an existing record, don't overwrite the identity column value.
To insert identity values into a table in SQL Server you must tell the database to allow you to do this. Syntax is:
Set Identity_Insert [table] ON
When you're done you need to turn it off again.
Set Identity_Insert [table] OFF

Related

INSERT INTO or UPDATE in case of condition

I am facing a problem at the moment, and I would like a quick solution for this.
I already found another answers about this issue but none had helped, I had read about INSERT INTO and INSERT ON DUPLICATE KEY UPDATE, but I think it doesn't really help me.
So the thing is, I have some Migrations enabled that Add new columns to existing tables, the thing is that the existing records on table prior to Migration get records with empty values even on existing columns (because I am changing the order of the columns on the new table migrated)
I am doing a SP that it's purpose is to fill the new Table migrated, according to Inserts or Updates.
I want to insert new records always except in the case that I found an existing record on the table with empty columns (column Id - primary key - is filled)
The "pseudo code" (mixing sql tables knowledge and c# syntax) would be something like this:
int i = 0;
foreach(Record item in importing_data_table)
{
if(i < tableMigrated.Length && tableMigrated[i].Column IS NULL)
{
UPDATE tableMigrated[i] SET Column = item.Column
}
else
{
INSERT INTO item
}
i++;
}
NOTE: Deleting all the rows from the tableMigrated before inserting is not possible, because there are foreign keys to that table. I got the following error trying that approach:
- The DELETE statement conflicted with the REFERENCE constraint "FK_blabla.Testing_testingTest.tableMigrated_Id".
NOTE2: Maybe my only option is using Cursors?
Thank you so much for your help in advance!
In your Stored Procedure, use the Merge Statement. Merge provides the functionality to INSERT based on your insert condition, otherwise UPDATE. Here is an example
MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
WHEN NOT MATCHED BY TARGET THEN
INSERT (CustomerID, ProductID, LastPurchaseDate)
VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
Take a look at Technet - Inserting, Updating, and Deleting Data by Using MERGE

Implement an efficent mechanism to check whether a record is referenced in other tables before delete

I am developing an web app using asp.net. I am making this app for compatable for both SQL and MYSQL DB.
So my concern is think that I have a set of records in a table. This table's records are referenced by other tables. So if a user try to delete a record from this table I have to check whether this record is referenced by other tables or not. If not then user can delete the record. I am using using foreign keys for many tables but others not.
So I want this scenario for every tables. So method that coming to my mind is before delete a record I have to run some select queries against those tables to check whether if records available. So is this the only approach.? Seems to its headache. you know if table is referenced by lot of tables. Can I use a flag or some thing?
Is there any better way to do this?
I think this might help you ::
SELECT
table_name, column_name
FROM
information_schema.key_column_usage
WHERE
referenced_table_name = '<table>'
and referenced_column_name = '<primary key column>'
Please check this link too:
MySQL: How to I find all tables that have foreign keys that reference particular table.column AND have values for those foreign keys?
I think it is a little overkill and not performance optimized to be selecting tables and references to check before each delete. You will be making unnecessary database calls.
Since you tag'd ASP.Net are you using ADO ? If so, or similar.
Why not make the normal delete call inside a try block and in the catch handle error message received from database something like:
try
{
}
catch(SqlExcpetion sqlEx)
{
if(sqlEx.Message.ToLower().Contains("foreign"))
{
return "your user friendly error message";
}
}
In case you are using foreign keys to constraint the references, you can act in the following order:
consider you are using database test and are trying to delete a row from emp table
1) list all the tables with their column names, that reference any column in the table we are going to remove a row from (emp in this case)
select
table_name,column_name,referenced_column_name
from
information_schema.KEY_COLUMN_USAGE
where
REFERENCED_TABLE_NAME = 'emp' and REFERENCED_table_schema = 'test';
2) for each row of the result try looking up the value of referenced_column_name from the emp row that is being removed in the corresponding table_name.column_name

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.

How Can I Programmatically Override the Auto_Increment Value When Inserting Into a DB

I have just started converting an old web application into a new structure.
I have a newly designed supplier table created in a MS SQL Server 2012 DB, which I am in the process of transferring the supplier data from the old DB to.
I have a C# MVC application set-up using the new DB as the data context. At present the application gets the data from the old applications DB, checks it, reformats it where necessary and inserts it into the new database in the desired format.
However as users of the application have got used to the supplier IDs in the old DB, I don't want to change the IDs, however as the id field in the new supplier table is an auto increment, it doesn't copy the ids from the old database. It just simply increments.
Does anybody know of anyway the auto increment can be overridden programmatically in the c# code for the initial import stage of the project. Once the data from the old system has been imported, I would then like to revert back to auto increment.
You can set identity insert on. However, if the users know the keys I've found they start asking to change them. I might consider the old keys as the user identifier and create a new surrogate key they won't ever see.
You can turn off the identity generation on the server temporarily during the bunch of inserts, then turn it on again. Try submitting this query to the server:
SET IDENTITY_INSERT dbo.sometable ON ;
INSERT INTO dbo.sometable (id,.......) VALUES (456,.......) ;
INSERT INTO dbo.sometable (id,.......) VALUES (276,.......) ;
INSERT INTO dbo.sometable (id,.......) VALUES (387,.......) ;
SET IDENTITY_INSERT dbo.sometable OFF ;
It just disables identity creation, then does the inserts (note that the column and value lists do include the id), then restores the default behavior.
In Sql Server, you can set the ID field to non identity. This will make it not auto increment

How to insert to table with one-to-one relationship via dataset

I use asp.net 4 and DataSets for accessing the database. There are two tables with one-to-one relationship in the database. It means that both tables have the same column as a primary key (say Id), and one of tables has #identity on this column set.
So in general if we want to insert, we insert first into the first table, than insert into the second table with id.table2 = id of the corresponding record in table1.
I can imagine how to achieve this using stored procedure (we would insert into the first table and have id as an out parameter and then insert into the second table using this id, btw all inside one transaction).
But is there a way to do it without using a stored procedure? May be DataSets \ DataAdapters have such functionality built in?
Would appreciate any help.
Today it is so quiet here... Ok if anybody is also looking for such a solution, I've found a way to do it.
So our main problem is to get the id of the newly created record in the first table. If we're able to do that, after that we simply supply it to the next method which creates a corresponding record in the second table.
I used a DataSet Designer in order to enjoy the code autogeneration feature of the VS. Let's call the first table TripSets. In DataSet Designer right click on the TripSetsTableAdapter, then Properties. Expand InsertCommand properties group. Here we need to do two things.
First we add a new parameter into the collection of parameters using the Parameters Collection Editor. Set ParameterName = #TripId, DbType = Int32 (or whatever you need), Direction = Output.
Second we modify the CommandText (using Query Builder for convenience). Add to the end of the command another one after a semicolon like that:
(...);
SELECT #TripId = SCOPE_IDENTITY()
So you will get something like this statement:
INSERT INTO TripSets
(Date, UserId)
VALUES
(#Date,#UserId);
SELECT #TripId = SCOPE_IDENTITY()
Perhaps you will get a parser error warning, but you can just ignore it. Having this configured now we are able to use in our Business logic code as follows:
int tripId;
int result = tripSetsTableAdapter.Insert(tripDate, userId, out tripId);
// Here comes the insert method into the second table
tripSetTripSearchTableAdapter.Insert(tripId, amountPersons);
Probably you will want to synchronize this operations somehow (e.g. using TransactionScope) but it is completely up to you.

Categories

Resources