I've done a POST method that takes object Recipe and inserts its values in the database using Dapper. I've written the SQL query so that when a new entry is inserted, the ID value will be autogenerated as: the maximum existing value in the database + 1. See code below:
using (var con = _connFactory())
{
con.Open();
con.Execute(#"INSERT INTO dbo.Recipe (Id, Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES ((SELECT MAX(ID) + 1 FROM dbo.Recipe), #name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig)",
new
{
...
...
});
}
When I send the request to the API using Postman, the returned result will be the newly created object that has been passed to the database. However, though, because of the way that my ID is created, the returned JSON looks like this:
{
"id": 0,
"name": "Test Recipe2",
"recipeLink": "testlink",
"category1Id": 7757,
...
"servingsUnitId": 3,
"servingsUnitOrig": null
}
As you can see, the ID is 0. But if I try to get the object after this, the ID will be set to the proper one, the one generated from (SELECT MAX(ID) + 1 FROM dbo.Recipe).
Is there any way in which I can make the API return the correct ID after the execution of the INSERT INTO? Or how should I change the logic behind the auto generation in order to achieve this?
It will come quite in handy if I was able to get the ID's value directly as now I've made a method that returns the ID of the latest Recipe created. But that might be a problem in case that there are 2 recipes created in a short period of time.
You can return the Id and use ExecuteScalar to get it. In the execute Statement declare & generate next Id and the the end select the NewId value
using (var con = _connFactory())
{
con.Open();
var addedId = con.ExecuteScalar(#"DECLARE #NewId INT = (SELECT ISNULL(MAX(ID),0) + 1 FROM dbo.Recipe);INSERT INTO dbo.Recipe (Id, Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES (#NewId, #name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig); SELECT #NewId",
new
{
...
...
});
//use addedId here...
}
I would strongly suggest that you change the Id behavior to use either SQL-Server Sequence or IDENTITY. Using select max() can generate duplicates
Select the id and call Query to retrieve it:
const string Sql = #"
INSERT INTO dbo.Recipe (...) VALUES (....);
SELECT CAST(SCOPE_IDENTITY() as int)";
int id = con.Query<int>(Sql, new { ...}).Single();
You can use SQL Auto Increment and change table
https://www.w3schools.com/sql/sql_autoincrement.asp
ALTER TABLE dbo.Recipe DROP COLUMN Id
ALTER TABLE dbo.Recipe ADD Id INT IDENTITY(1,1)
After that, you don't need a pass id field in your query.
con.Execute(#"INSERT INTO dbo.Recipe (Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES (#name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig)"
You have trouble because of your first record
SELECT ISNULL (MAX (ID), 0) + 1 FROM [dbo]. [Recipe]
I am inserting records in table with inline query.I have to get Identity generated by Insert statement by using Output Parameter, this is how I am doing
//Com.CommandText contains Insert Statment
OleDbParameter IDParameter = new OleDbParameter("#ID", OleDbType.Integer);
IDParameter.Direction = ParameterDirection.Output;
Com.Parameters.Add(IDParameter);
Com.ExecuteNonQuery();
After executing when I am checking the output parameter value,its appearing 0, althought record is inserting properly
this is how I am checking
Com.Parameters[0].Value
I also tried this
Com.Parameters["#ID"].Value
But output parameter value is always 0
I also tried using Select Scope_Identity() inside Insert statment but no luck,
I have also triend ExecuteScalar() but still same issue
Your insert query must end with
SELECT CAST(scope_identity() AS int)
Then ExecuteScaler() will return the identity.
int ID = (int)cmd.ExecuteScalar();
I am importing data from large excel sheet and storing it in a stateTable. Now I have to push this data into a database table. The table does have an identity column(1,1).
I have created a similar table type in DB and a procedure to take a parameter as table type to insert in the particular table. I have also set ON the identity insert.
My code is:
using (SqlCommand command = new SqlCommand("InsertStateTable") {
CommandType = CommandType.StoredProcedure})
{
SqlParameter param = command.Parameters.AddWithValue("#statetable", dt);
param.TypeName = "StateTable";
param.SqlDbType = SqlDbType.Structured;
command.Connection = con;
con.Open();
command.ExecuteNonQuery();
con.Close();
}
But the error that arises is
"INSERT into an identity column not allowed on table variables."
I have gone thru many sites but no specific reason is given.....
Thanks in advance.
The error is fairly clear: you are not allowed to do what you are trying to do. Basically, you are going to have to find a design that is not dependent on inserting the identity value into the table-variable / table-valued-parameter. My advice would be to create a separate table-variable (unrelated to the table-valued-parameter) which has the same data, but which does not have the IDENTITY column, so...
declare #foo table (id int not null, name nvarchar(200) not null /* etc */)
insert #foo (id, name /* etc */)
select id, name /* etc */ from #statetable
at which point #foo has the original data, but does not have an identity column - you can then do whatever you want with #foo.
Without seeing what you are doing with your identity insert, it is hard to comment much further.
I'm working on an ASP.NET project (C#) with SQL Server 2008.
When I insert a row into a table in the database, I would like to get the last inserted ID, which is the table's IDENTITY (Auto Incremented).
I do not wish to use another query, and do something like...
SELECT MAX(ID) FROM USERS;
Because - even though it's only one query - it feels lame...
When I insert something I usually use ExecuteNonQuery(), which returns the number of affected rows.
int y = Command.ExecuteNonQuery();
Isn't there a way to return the last inserted ID without using another query?
Most folks do this in the following way:
INSERT dbo.Users(Username)
VALUES('my new name');
SELECT NewID = SCOPE_IDENTITY();
(Or instead of a query, assigning that to a variable.)
So it's not really two queries against the table...
However there is also the following way:
INSERT dbo.Users(Username)
OUTPUT inserted.ID
VALUES('my new name');
You won't really be able to retrieve this with ExecuteNonQuery, though.
You can return the id as an output parameter from the stored procedure, e.g. #userId int output
Then, after the insert, SET #userId = scope_identity()
even though it's only one query - it feels lame...
It actually is also wrong as you can have multiple overlapping iserts.
That is one thing that I always fuind funny - people not reading the documentation.
SELECT SCOPE_IDENTITY()
returns the last identity value generated in a specific scope and is syntactically correct. It also is properly documented.
Isn't there a way to return the last inserted ID without using another query?
Yes. Ask for the number in the saame SQL batch.
INSERT (blablab9a); SELECT SCOPE_IDENTITY ();
as ONE string. ExecuteScalar.
You can have more than one SQL statement in one batch.
If you want to execute query from C# code & want to get last inserted id then you have to find the following code.
SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
connection.Open();
string sql = "Insert into [Order] (customer_id) values (" + Session["Customer_id"] + "); SELECT SCOPE_IDENTITY()";
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
var order_id = cmd.ExecuteScalar();
connection.Close();
Console.Write(order_id);
I'm trying to insert a row into a PostgreSQL table with a serial primary key and I need to retrieve this column after it was inserted. I got something like this:
The table "pais" has 3 columns: id, pais, capital; id is a serial column and is its primary key.
NpgsqlCommand query = new NpgsqlCommand("insert into pais(nombre, capital) values(#nombre, #capital)", conn);
query.Parameters.Add(new NpgsqlParameter("nombre", NpgsqlDbType.Varchar));
query.Parameters.Add(new NpgsqlParameter("capital", NpgsqlDbType.Varchar));
query.Prepare();
query.Parameters[0].Value = this.textBox1.Text;
query.Parameters[1].Value = this.textBox2.Text;
Object res = query.ExecuteScalar();
Console.WriteLine(res);
It inserts the row on the table but "res" value is null. If I insert with the nexval('table_sequence') also returns null.
Any idea of how can I return the id of the table? Am I missing something?
Thanks in advance
Is that thread safe?
What if another insert happens between your insert and select?
Why not use:
INSERT INTO table (fieldnames) VALUES (values) RETURNING idcolumn?
insert into pais(nombre, capital) values(#nombre, #capital) RETURNING id
replace id with your primary keyenter code here and use
Object res = query.ExecuteScalar();
Inside res you'll have the PK.
In order to select the last identity inserted you need to use: currval(sequencename)
so your select statement should look like:
NpgsqlCommand query = new NpgsqlCommand("insert into pais(nombre, capital) values(#nombre, #capital);select currval('table_sequence');", conn);
The insert itself does not cause a value to be returned. When you perform ExecuteScalar it is looking for a single value to be "Selected" so to speak.
I believe you need to follow up your insert with a select statement to solve your issue.
If you were using t-sql you would do this like so
string sql =
"INSERT INTO [Table] (FieldName) VALUES (#ParamName); "
+ "SELECT CAST(scope_identity() AS int)";
ExecuteScalar would then return the unique id;
I am not sure of the exact syntax for postGresql but hopefully this allows you to solve your issue.