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]
Related
I am inserting a row into one table then want to get that new ID so I can add it in another variable where I have email address stored.
var db = Database.Open("myDB");
var insertCommand1 = "INSERT INTO myDB (FirstName, LastName) Values(#0, #1)";
db.Execute(insertCommand1, first, last);
var lastInsertedId = db.QueryValue("SELECT SCOPE_IDENTITY()");
var insertCommand2 = "INSERT INTO email (id_person, email) Values(#0, #1)";
db.Execute(insertCommand2, lastInsertId, email);
where id_person is the id that is created in my first table. When I run the code I get lastInsertedId = {}. Any reason why it is not grabbing a value for id_person which is a primary key, int , not null for my first table? --Tim
From the documentation of SCOPE_IDENTITY(), emphasis mine:
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.
Because you are using two queries they are considered two batches. You need to do your insert and your select in a single query.
I don't know what library you are using, so I am just guessing on the syntax but I beleive you need something like
var db = Database.Open("myDB");
var insertCommand1 = "INSERT INTO myDB (FirstName, LastName) Values(#0, #1); " +
"SELECT SCOPE_IDENTITY()";
var lastInsertedId = db.QueryValue(insertCommand1, first, last);
var insertCommand2 = "INSERT INTO email (id_person, email) Values(#0, #1)";
db.Execute(insertCommand2, lastInsertId, email);
It is very easy to get the SCOPE_IDENTITY() value back from SQL Server. I will give an example where I was able to print the SCOPE_IDENTITY() data back in c# label.
My Code Snippet in a Submit from data insert
btnSubmit_Click()
{
Random s = new Random();
cmd = new SqlCommand("INSERT INTO [dbo].[tblMemberAccount] ([Userid], [User_pwd], [User_mobile], [User_referal], [UserWallet]) VALUES(#Userid, #User_pwd, #User_mobile, #User_referal, #UserWallet) ​select scope_identity()", cons);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Userid",s.Next(4506,9999));
cmd.Parameters.AddWithValue("#User_pwd",txtPassword.Text);
cmd.Parameters.AddWithValue("#User_mobile",txtPhoneNumber.Text);
cmd.Parameters.AddWithValue("#User_referal",txtReferral.Text);
cmd.Parameters.AddWithValue("#UserWallet",10);
cons.Open();
int g = Convert.ToInt32(cmd.ExecuteScalar());
cons.Close();
lblConfirm.Text = "Member " +g+ " added successfully!";
}
Here the value 'g' is returning the scope_identity value.
If the database is in SQL SERVER , create a SQL parameter and set the direction to
"Output".
Please check this link :
Getting the identity of the most recently added record
Using Text Area I'm sending Values to database by clicking button, but I want postID to auto generate how I can do it?
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostID,PostDet,Votes,UserID) VALUES('<auto Genatare post ID>','" + TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
You could make PostID a UNIQUEIDENTIFIER column and then pass in a newly generated GUID (Guid.NewGuid()).
Also, please use parameterized queries to avoid SQL injection. Especially if the inputs come directly from WEB users.
To do so, change your ConnectDataBaseToInsert method to not take SQL text, but an SqlCommand which you prepare with the respective parameters.
From your comment to the question: The PostID should be like PO0001. Then the only way to do it properly and to respect for concurrency is to generate a stored procedure that takes the value to insert, which generates the ID itself.
To do so, create a new table that contains the last post ID. Then, use an UPDATE ... OUTPUT statement to increment and return in one go. This is the only way to do an atomic update of the post ID so that no two users create the same ID.
Example Table PostIDTable
Current
=======
0
Example SELECT to update and retrieve the current post ID:
-- We need a temp table, because OUTPUT can not output into a single variable
-- This increments Current by one and outputs the value that was set in one go.
-- This prevents simultaneous calls to get the same ID
DECLARE #postID (ID INT)
UPDATE PostIDTable
OUPUT INSERTED.Current INTO #postID
SET
Current = Current + 1
-- Get the value from the temp table and convert it into the desired format
DECLARE #pID INT = (SELECT TOP 1 ID FROM #postID)
DECLARE #id NVARCHAR(6) = 'PO' + RIGHT('0000' + CONVERT(NVARCHAR, #pID), 4)
-- Do the actual INSERT
INSERT INTO (PostDet, Votes,UserID) VALUES (#id, ..., ...)
You should make the PostID column to be an IDENTITY(1,1) column (in case of using MSSQL. server). So when you will insert new rows to your database the corresponding PostID values will be autogenerated by your database. The same holds for each other RDBMS you may use.
Having done the above change you code will change to the following:
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostDet,Votes,UserID) VALUES("TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
If PostId is autogenerated, only do this:
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostDet,Votes,UserID) VALUES('" + TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
Removing PostId from your query, it will generate the next Id
the best way is to use auto increment column ID for your table.
Please, see w3school tutorial where is described construction for all main db.
Try this..
select * from tablename order by id desc limit 1
where id would be your primary key attribute name.
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.
I know that in Oracle I can get the generated id (or any other column) from an inserted row as an output parameter.
Ex:
insert into foo values('foo','bar') returning id into :myOutputParameter
Is there a way to do the same, but using ExecuteScalar instead of ExecuteNonQuery?
I don't want to use output parameters or stored procedures.
ps: I'm using Oracle, not sql server!!!
If you are on oracle, you have to use ExecuteNonQuery and ResultParameter. There is no way to write this as query.
using (OracleCommand cmd = con.CreateCommand()) {
cmd.CommandText = "insert into foo values('foo','bar') returning id into :myOutputParameter";
cmd.Parameters.Add(new OracleParameter("myOutputParameter", OracleDbType.Decimal), ParameterDirection.ReturnValue);
cmd.ExecuteNonQuery(); // an INSERT is always a Non Query
return Convert.ToDecimal(cmd.Parameters["myOutputParameter"].Value);
}
Oracle uses sequences as for his identity columns, if we may say so.
If you have set a sequence for your table primary key, you also have to write a trigger that will insert the Sequence.NextValue or so into your primary key field.
Assuming that you are already familiar with this concept, simply query your sequence, then you will get your answer. What is very practiced in Oracle is to make yourself a function which will return an INT, then within your function, you perform your INSERT. Assuming that you have setup your trigger correctly, you will then be able to return the value of your sequence by querying it.
Here's an instance:
CREATE TABLE my_table (
id_my_table INT PRIMARY KEY
description VARCHAR2(100) NOT NULL
)
CREATE SEQUENCE my_table_seq
MINVALUE 1
MAXVALUE 1000
START WITH 1
INCREMENT BY 2
CACHE 5;
If you want to manage the auto-increment yourself, here's how:
INSERT INTO my_table (
id_my_table,
description
) VALUES (my_table_seq.NEXTVAL, "Some description");
COMMIT;
On the other hand, if you wish not to care about the PRIMARY KEY increment, you may proceed with a trigger.
CREATE OR REPLACE TRIGGER my_table_insert_trg
BEFORE INSERT ON my_table FOR EACH ROW
BEGIN
SELECT my_table_seq.NEXTVAL INTO :NEW.id_my_table FROM DUAL;
END;
Then, when you're inserting, you simply type the INSERT statement as follows:
INSERT INTO my_table (description) VALUES ("Some other description");
COMMIT;
After an INSERT, I guess you'll want to
SELECT my_table_seq.CURRVAL
or something like this to select the actual value of your sequence.
Here are some links to help:
http://www.orafaq.com/wiki/Sequence
http://www.orafaq.com/wiki/AutoNumber_and_Identity_columns
Hope this helps!
You can use below code.
using (OracleCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"INSERT INTO my_table(name, address)
VALUES ('Girish','Gurgaon India')
RETURNING my_id INTO :my_id_param";
OracleParameter outputParameter = new OracleParameter("my_id_param", OracleDbType.Decimal);
outputParameter.Direction = ParameterDirection.Output;
cmd.Parameters.Add(outputParameter);
cmd.ExecuteNonQuery();
return Convert.ToDecimal(outputParameter.Value);
}
one possible way if one can add one column named "guid" to the table :
when inserting one record from c#, generate a guid and write it to the guid column.
then perform a select with the generated guid, and you have got the id of inserted record :)
Select t.userid_pk From Crm_User_Info T
Where T.Rowid = (select max(t.rowid) from crm_user_info t)
this will return your required id