Adding SQL Server ID Increment upon registration - c#

How would I retrieve an Id from my SQL Server table, add +1 to it, and register that number to the next Id when someone registers into my program?
This is my code so far. The ?? means I don't know what would go there for this to work.
SqlCommand hi = new SqlCommand("SELECT MAX(Id) FROM Table");
hi.CommandType = CommandType.Text;
hi.Connection = connection;
hi.Parameters.AddWithValue("#Id", ??);
For example, last Id was 14. Now someone is registering and I want it to say 15 in the SQL Server table under Id.

I'm not sure what your full table structure is like, but the functionality you're describing is available natively in a SQL Server databases, and it's called an identity column. The way you would implement it is like this:
CREATE TABLE MyTable
(
Id int not null identity(1,1),
Name nvarchar(255) not null
# other columns go here...
)
You should be able to alter an existing table to make an Id column use identity, the only catch is you can only have one identity column per table.
When you want to insert a new record into the table, you can leave the identity column out of your insert statement, as SQL Server will fill it with the appropriate value:
INSERT INTO MyTable (Name)
VALUES ('My Name')

I would suggest to handle it at database level by making ID column auto increment.
If you want to do it from your code then you have to make additional hit to database to get last ID before you make your insert call to add data.

Related

How do I add 1 to a value after it's inserted into SQL Server table in C#?

Especially for the columns with ID's, I want to add a value of 1 after I inserted a ID into a SQL Server table, so it will be ID +1. Now I can't figure out a way to do this. Does anyone have tips?
In the table creation script, set that column as identity, so you don't need to mention the column name in the insert script, and the SQL server will automatically fill its value with the maximum value +1.
Here is an example of table creation and how to insert some data into it, and what the result will be.
Creation
create table tablename (id int identity,name nvarchar(max))
Insertion
Insert Into TableName(name) values('Test1')
Insert Into TableName(name) values('Test2')
Result
ID Name
1 Test1
2 Test2

How to store data into two tables with same ID? [duplicate]

How am I supposed to get the IDENTITY of an inserted row?
I know about ##IDENTITY and IDENT_CURRENT and SCOPE_IDENTITY, but don't understand the implications or impacts attached to each.
Can someone please explain the differences and when I would be using each?
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes. You need to be careful here, since it's across scopes. You could get a value from a trigger, instead of your current statement.
SCOPE_IDENTITY() returns the last identity value generated for any table in the current session and the current scope. Generally what you want to use.
IDENT_CURRENT('tableName') returns the last identity value generated for a specific table in any session and any scope. This lets you specify which table you want the value from, in case the two above aren't quite what you need (very rare). Also, as #Guy Starbuck mentioned, "You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into."
The OUTPUT clause of the INSERT statement will let you access every row that was inserted via that statement. Since it's scoped to the specific statement, it's more straightforward than the other functions above. However, it's a little more verbose (you'll need to insert into a table variable/temp table and then query that) and it gives results even in an error scenario where the statement is rolled back. That said, if your query uses a parallel execution plan, this is the only guaranteed method for getting the identity (short of turning off parallelism). However, it is executed before triggers and cannot be used to return trigger-generated values.
I believe the safest and most accurate method of retrieving the inserted id would be using the output clause.
for example (taken from the following MSDN article)
USE AdventureWorks2008R2;
GO
DECLARE #MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM #MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
I'm saying the same thing as the other guys, so everyone's correct, I'm just trying to make it more clear.
##IDENTITY returns the id of the last thing that was inserted by your client's connection to the database.
Most of the time this works fine, but sometimes a trigger will go and insert a new row that you don't know about, and you'll get the ID from this new row, instead of the one you want
SCOPE_IDENTITY() solves this problem. It returns the id of the last thing that you inserted in the SQL code you sent to the database. If triggers go and create extra rows, they won't cause the wrong value to get returned. Hooray
IDENT_CURRENT returns the last ID that was inserted by anyone. If some other app happens to insert another row at an unforunate time, you'll get the ID of that row instead of your one.
If you want to play it safe, always use SCOPE_IDENTITY(). If you stick with ##IDENTITY and someone decides to add a trigger later on, all your code will break.
The best (read: safest) way to get the identity of a newly-inserted row is by using the output clause:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare #IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into #IdentityOutput
values
( ... )
select #IdentityValue = (select ID from #IdentityOutput)
Add
SELECT CAST(scope_identity() AS int);
to the end of your insert sql statement, then
NewId = command.ExecuteScalar()
will retrieve it.
From MSDN
##IDENTITY, SCOPE_IDENTITY, and IDENT_CURRENT are similar functions in that they return the last value inserted into the IDENTITY column of a table.
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope. For more information, see IDENT_CURRENT.
IDENT_CURRENT is a function which takes a table as a argument.
##IDENTITY may return confusing result when you have an trigger on the table
SCOPE_IDENTITY is your hero most of the time.
When you use Entity Framework, it internally uses the OUTPUT technique to return the newly inserted ID value
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, joined back to the table, and return the row value out of the table.
Note: I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
This technique (OUTPUT) is only available on SQL Server 2008 or newer.
Edit - The reason for the join
The reason that Entity Framework joins back to the original table, rather than simply use the OUTPUT values is because EF also uses this technique to get the rowversion of a newly inserted row.
You can use optimistic concurrency in your entity framework models by using the Timestamp attribute: 🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
When you do this, Entity Framework will need the rowversion of the newly inserted row:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
And in order to retrieve this Timetsamp you cannot use an OUTPUT clause.
That's because if there's a trigger on the table, any Timestamp you OUTPUT will be wrong:
Initial insert. Timestamp: 1
OUTPUT clause outputs timestamp: 1
trigger modifies row. Timestamp: 2
The returned timestamp will never be correct if you have a trigger on the table. So you must use a separate SELECT.
And even if you were willing to suffer the incorrect rowversion, the other reason to perform a separate SELECT is that you cannot OUTPUT a rowversion into a table variable:
DECLARE #generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO #generated_keys
VALUES('Malleable logarithmic casing');
The third reason to do it is for symmetry. When performing an UPDATE on a table with a trigger, you cannot use an OUTPUT clause. Trying do UPDATE with an OUTPUT is not supported, and will give an error:
Cannot use UPDATE with OUTPUT clause when a trigger is on the table
The only way to do it is with a follow-up SELECT statement:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE ##ROWCOUNT > 0 AND TurboEncabulatorID = 1
I can't speak to other versions of SQL Server, but in 2012, outputting directly works just fine. You don't need to bother with a temporary table.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
By the way, this technique also works when inserting multiple rows.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
Output
ID
2
3
4
##IDENTITY is the last identity inserted using the current SQL Connection. This is a good value to return from an insert stored procedure, where you just need the identity inserted for your new record, and don't care if more rows were added afterward.
SCOPE_IDENTITY is the last identity inserted using the current SQL Connection, and in the current scope -- that is, if there was a second IDENTITY inserted based on a trigger after your insert, it would not be reflected in SCOPE_IDENTITY, only the insert you performed. Frankly, I have never had a reason to use this.
IDENT_CURRENT(tablename) is the last identity inserted regardless of connection or scope. You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into.
ALWAYS use scope_identity(), there's NEVER a need for anything else.
One other way to guarantee the identity of the rows you insert is to specify the identity values and use the SET IDENTITY_INSERT ON and then OFF. This guarantees you know exactly what the identity values are! As long as the values are not in use then you can insert these values into the identity column.
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
This can be a very useful technique if you are loading data from another source or merging data from two databases etc.
Create a uuid and also insert it to a column. Then you can easily identify your row with the uuid. Thats the only 100% working solution you can implement. All the other solutions are too complicated or are not working in same edge cases.
E.g.:
1) Create row
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2) Get created row
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
Even though this is an older thread, there is a newer way to do this which avoids some of the pitfalls of the IDENTITY column in older versions of SQL Server, like gaps in the identity values after server reboots. Sequences are available in SQL Server 2016 and forward which is the newer way is to create a SEQUENCE object using TSQL. This allows you create your own numeric sequence object in SQL Server and control how it increments.
Here is an example:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
Then in TSQL you would do the following to get the next sequence ID:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
Here are the links to CREATE SEQUENCE and NEXT VALUE FOR
Complete solution in SQL and ADO.NET
const string sql = "INSERT INTO [Table1] (...) OUTPUT INSERTED.Id VALUES (...)";
using var command = connection.CreateCommand();
command.CommandText = sql;
var outputIdParameter = new SqlParameter("#Id", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(outputIdParameter);
await connection.OpenAsync();
var outputId= await command.ExecuteScalarAsync();
await connection.CloseAsync();
int id = Convert.ToInt32(outputId);
After Your Insert Statement you need to add this. And Make sure about the table name where data is inserting.You will get current row no where row affected just now by your insert statement.
IDENT_CURRENT('tableName')

how to insert same LAST_INSERT_ID() in one table to create multiple records;

I am programming a website, with ASP.NET, C#, as well as using MYSQL.
I need to be able to record two rows with same child_id;
My problem is with this one statement, it inserts one row and skips the last.
//data.ChldrenRecord.Service contain two records
// babysit, and tutor
foreach (string s in data.ChildrenRecord.Services)
{
query += (" INSERT INTO ServiceChildren SET service='" + s + "', child_id=LAST_INSERT_ID();");
}
so table should look something like this
id service child_id
1 babysit 1
2 tutor 1
I use Last_INSERT_ID() because child_id is a foreign key. I create a record in another table whose primary key is child_id. Afterwards, I use LAST_INSERT_ID() to reference that one record primary key child_id so that i may use it in my ServiceChildren table.
as it stands my table looks:
id service child_id
1 babysit 1
I think i am on the right track now. if i log directly into the database, this statement works:
SET #last_id = LAST_INSERT_ID();
//then insert statements
INSERT INTO ServiceChildren(service, child_id) VALUES('babysit', #last_id);
INSERT INTO ServiceChildren(service, child_id) VALUES('tutor', #last_id);
BUT IN MY C#, ASP.NET CODE THE LINE
SET #last_id = LAST_INSERT_ID();
does not do anything, even populate tables with a prior insert statement.
OK, I found out my problem. I needed to place Allow User Variables=True in the connection string to be allowed to create and use #last_id.

Insert 2 rows in tables with foreign key relation without knowing the primary key of the main table

I have the following databasescheme in SQL Server Manager 2014.
I'm making a C#-windows application in Visual Studio and I want to insert a new orderline and a new order. The problem is that the primary keys of both tables, auto-generate in server manager, so I haven't yet the value of the primary key of the order-table, but I need that value to fill into the foreign key of the orderLine column. How can I insert these two rows.
Kind regards
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newOrderID;
var cmd = "INSERT INTO Order (column_name) VALUES (#Value) ;SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newOrderID = (int)insertCommand.ExecuteScalar();
}
}
This will allow you to catch the last generated OrderId and use it in the Insert Statement for the OrderLine table.
Another option is to use the following SQL code:
string command = "INSERT INTO Order(totalPrice) OUTPUT INSERTED.ID VALUES(#totalPrice)" // this will be a parameter from your code
Then the OrderId can be taken from :
Int32 orderId = (Int32) command.ExecuteScalar();
While scope_id() works fine for single rows, you really should learn to use the output clause as scope_id() is useless for multiple rows inserted with a single sql statement.
See this prior question for a simple example of using the output clause.
Obviously this allows you to retrieve more than just the identity value too.
ADDED
Also useful is the new sequence feature (added for 2012) instead of using identity. If your are coming from other databases this may seem a more natural solution.
Sequence is very useful if you would like to share a single sequence among several tables -- although this is an uncommon design I have used it a few times.
If you're using any form of direct SQL, you need to receive the SCOPE_IDENTITY() value immediately after inserting your order, then use that value to insert your lines.
INSERT INTO Order
SELECT SCOPE_IDENTITY() AS NewId; OR RETURN SCOPE_IDENTITY(); OR DECLARE #OrderId INT = SCOPE_IDENTITY();
INSERT INTO OrderLine
Otherwise, use Entity Framework and it will automatically retrieve your new IDs and assign to dependencies.

How to handle duplicates in SQL Server

I have a table in SQL Server called Test_Table with two columns: ID and Name
The table looks something like this:
ID NAME
--------
1 John
2 Jane
Now I have a stored procedure which inserts records into this.
INSERT INTO Test_Table
VALUES (#Id,#Name)
And I'm passing this values from my c# code. Now I want to modify this so that the table does not have duplicates. Where should I check this, In the code or the DB? I'm very weak in DB side stuff. So how can I handle duplicates before inserting values in my table
The "right" way to do that is in DB because:
Don't need to read all DB
Need to pass all data to C# which increase the IO
Concurrency - if you have more than 1 C# application you will need to sync them vs in DB it would be simpler
You can define the column as unique or key, which will prevent duplicate values ,DB will take care of it
If you use MSSQL use UNIQUE Constraints
Read this good answer about avoid duplicates
You should do this check in the database. Always, if you want it to be true of the data.
I'm not sure what you consider a duplicate. Normally, an id column would be an identity column that is automatically incremented for each value. This would prevent duplicates. You would define it as:
create table test_table (
id int not null identity(1, 1),
. . .
Then, you would insert into it using:
insert into test_table(name)
values (#Name);
The id would be assigned automatically.
If you want no duplicates just for name, then create a unique index or unique constraint (really the same thing). You can do this in the table definition just by adding unique to the column:
create table test_table (
id int not null identity(1, 1),
name varchar(255) unique
. . .
Or by creating a unique index after you have created the table:
create index test_table_name on test_table(name)
(Or by explicitly creating a constraint, which is another method.)
In either case ,you will have to access to database to check wheteher values exist already.
IF NOT EXISTS (SELECT * FROM Test_Table WHERE ID= #ID AND Name=#Name)
BEGIN
INSERT INTO Test_Table
VALUES (#Id,#Name)
END
If it is possible to make ID column as unique you can avoid checking as insertion would.t be allowed for repeating ID values , in that case you will have to handle error.
See this thread how to handle violation of Unique key constraint.
If you don't want repeating IDs you'll have to set the ID as the Primary Key, which is pretty much obligatory.
If you don't want the Name to repeat, you could populate a list with the Names the table contains, and then you would only insert whatever name is not in that List.
Here is an example, instead of using a list I used a dictionary:
Dictionary<int, string> Names = new Dictionary<int, string> ();
using (SqlCommand command = new SqlCommand ("SELECT * FROM TestTable", con))
using (SqlDataReader reader = command.ExecuteReader ()) {
while (reader.Read ()) {
Names.Add (reader["ID"], reader["NAME"]);
}
}
if (!Names.ContainsValue ("ValueYouWantToInsert")) {
//do stuff
}
You should check it in DB, Also you can make ID as Primary Key
Which is mostly used, because people can have duplicate name.
You can modify your Id with the Unique key constraint or you can also make it Primary key.
Try like this:
alter table Test_Table add primary key (ID)
and
alter table Test_Table add unique key (Name)
IF NOT EXISTS (SELECT * FROM Test_Table WHERE ID= #ID AND Name=#Name)
BEGIN
INSERT INTO Test_Table
VALUES (#Id,#Name)
END
ELSE
BEGIN
UPDATE Test_Table
SET ID= #ID,NAME = #Name
WHERE ID= #ID AND Name=#Name
END

Categories

Resources