How to get the generated id from an inserted row using ExecuteScalar? - c#

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

Related

ID variable not shown after creating object and passing it to database

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]

Get Identify specification of Insert Query ado.net

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();

INSERT into an identity column not allowed on table variables

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.

Return last inserted ID without using a second query

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);

Retrieve serial ID with Npgsql when inserting with ExecuteScalar

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.

Categories

Resources