I'm using the MySql Connector .net, and I need to get the insert id generated by the last query. Now, I assume the return value of MySqlHelper.ExecuteNonQuery should be the last insert id, but it just returns 1.
The code I'm using is:
int insertID = MySqlHelper.ExecuteNonQuery(Global.ConnectionString,
"INSERT INTO test SET var = #var", paramArray);
However insertID is always 1. I tried creating a MySql connection and opening/closing manually which resulted in the same behaviour
Just use LastInsertedId field
MySqlCommand dbcmd = _conn.CreateCommand();
dbcmd.CommandText = sqlCommandString;
dbcmd.ExecuteNonQuery();
long imageId = dbcmd.LastInsertedId;
1 is the no of records effected by the query here only one row is inserted so 1 returns
for getting id of the inserted row you must use scope_identity() in sqlserver and LAST_INSERT_ID() in MySql
Try to use this query to get last inserted id -
SELECT LAST_INSERT_ID();
Then, run DbCommand.ExecuteReader method to get IDataReader -
command.CommandText = "SELECT LAST_INSERT_ID()";
IDataReader reader = command.ExecuteReader();
...and get information from the reader -
if (reader != null && reader.Read())
long id = reader.GetInt64(0);
...do not forget to close the reader;-)
I had the same problem, and after some testing, I found out that the problem seem to be the connection method; you are using a connection string.
This is of course to make use of the automatic connection pool reuse, but in this case it gave me trouble.
The final solution for me is to create a new connection, execute the insert query, and then execute the last_insert_id(). On the same connection.
Without using the same connection, last_insert_id() might return anything, I don't know why, but guess it looses track of things as it can be different connections.
Example:
MySqlConnection connection = new MySqlConnection(ConnectionString);
connection.Open();
int res = MySqlHelper.ExecuteNonQuery(
connection,
"INSERT INTO games (col1,col2) VALUES (1,2);");
object ores = MySqlHelper.ExecuteScalar(
connection,
"SELECT LAST_INSERT_ID();");
if (ores != null)
{
// Odd, I got ulong here.
ulong qkwl = (ulong)ores;
int Id = (int)qkwl;
}
I hope this helps someone!
I know this is an old post, but I have been facing the same issue as Snorvarg. Using MySqlHelper, and using a connection string instead of a Connection object (to allow MySqlHelper to use connection pooling), SELECT LAST_INSERT_ID() would often give me the ID of the previous query that was executed, or other times it would return zero. I would then have to call SELECT LAST_INSERT_ID() a second time to get the correct ID.
My solution was to encapsulate everything between the query that's being executed, and the calling of SELECT LAST_INSERT_ID() in a TransactionScope. This forces MySqlHelper to stick to one connection instead of opening two separate connections.
So:
string sql = "INSERT INTO games (col1,col2) VALUES (1,2);");
string connectionString = "some connection string";
using (TransactionScope scope = new TransactionScope)
{
int rowsAffected = MySqlHelper.ExecuteNonQuery(connectionString, sql);
object id = MySqlHelper.ExecuteScalar(connectionString, "SELECT LAST_INSERT_ID();");
scope.Complete();
}
try below working solution in repository .
string query = $"INSERT INTO `users`(`lastname`, `firstname`, `email`, `createdate`, `isdeleted`) " +
$"VALUES ('{userEntity.LastName}','{userEntity.FirstName}','{userEntity.Email}','{userEntity.CreateDate}',{userEntity.IsDeleted});" +
$"SELECT LAST_INSERT_ID();";
var res= _db.ExecuteScalar(query);
return (int)(UInt64)res;
Related
I'm building a WinForms project in C# using a PostgreSQL database and the Npgsql framework.
For inserting a record, I need to return the ID of the new record. This SO question says to add SELECT SCOPE_IDENTITY() to the query string passed to cmd. So my query string looks like this:
string insertString = "INSERT INTO sometable (company_name, category, old_value, old_desc, new_value, new_desc, reference1, reference2) VALUES (#comp, #cat, #oldValue, #oldDesc, #newValue, #newDesc, #ref1, #ref2); SELECT SCOPE_IDENTITY();";
and then get the ID with something like
int modified = cmd.ExecuteNonQuery();
But that's likely SQL Server-specific. If I use that method, I get an exception at the above line saying, "fuction scope_identity() does not exist".
I wasn't able to find anything that seemed to address this on the Npgsql documentation.
Per the linked SO question and Denis' suggestions I've tried adding both
RETURNING id;
and
CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
to the query string, replacing SELECT SCOPE_IDENTITY(); with those statements in the code above. In both cases they work as intended in DBeaver on an insert, but in my C# code in my WinForm project, modified was set to "1".
NOTE: I re-titled the question and added more information about what I've done.
Add "returning idcolumn" to the end of the sql query, then run the command with the ExecuteScalar() method instead of ExecuteNonQuery(). It should return with an int.
string insert = "insert into table1 (col1) values (something) returning idcol";
int id = cmd.ExecuteScalar();
All the comments above were almost nearly spot on and got me to a solution but didn't exactly wrap it in a bow -- so I thought i'd post my implementation that works (with silly fake example tables of course).
private int? InsertNameIntoNamesTable(string name)
{
int? id = null;
using (var dbcon = new NpgsqlConnection(_connectionString))
{
dbcon.Open();
StringBuilder sb = new StringBuilder();
var sql = $#"
insert into names_table
(name)
values
({name})
returning id;
";
sb.Append(sql);
using (var cmd = new NpgsqlCommand(sql, dbcon))
{
id = (int)cmd.ExecuteScalar();
}
dbcon.Close();
}
return id;
}
My Problem has Been Fixed, My main problem was getting the information from the textbox in the xaml which got erased after that window was closed and another opened. Though the answers did fix my other problems and have made my code much simpler and easier to read. So thank you very much!
So I am Currently working on building a Calendar for a personal project and working on adding events to a Database, this table for Events stores two varchars, and an int (name, description, userid), the userid is a foreign key and is linked to the User Table. When I use the code below to try and pull the userid for the username that the person entered, it tells me that there is no existing value.
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString =
"Data Source=calenderserver.database.windows.net;" +
"Initial Catalog=Calender;" +
"User id=*******;" +
"Password=*******;" +
"MultipleActiveResultSets = true";
connection.Open();
SqlCommand com = new SqlCommand("Select UserId from Users Where UserName = #user", connection);
com.Parameters.AddWithValue("#user", UsernameTextBox.Text);
SqlDataReader reader = com.ExecuteReader();
reader.Read();
int userid = reader.GetInt32(1);
messages.Text = "Event Added";
SqlCommand command = new SqlCommand("INSERT INTO [Events] VALUES (#eventname, #eventdesc)", connection);
command.Parameters.AddWithValue("#eventname", name);
command.Parameters.AddWithValue("#eventdesc", description);
command.Parameters.AddWithValue("#userid", userid);
command.ExecuteNonQuery();
reader.Close();
connection.Close();
}
Even though when I run the same command in an actual SQL Query it returns a proper value.
SQL Command
I am completely lost on this and have checked multiple sources and solutions and would really appreciate the help.
You are doing int userid = reader.GetInt32(1); the indexes for the get function are 0 based so you actually need int userid = reader.GetInt32(0); so you get the first column.
That being said, because you are using the first result of the first column you can simplify your code by switching from a data reader to using ExecuteScalar()
SqlCommand com = new SqlCommand("Select UserId from Users Where UserName = #user", connection);
com.Parameters.AddWithValue("#user", UsernameTextBox.Text);
int userid = (int)com.ExecuteScalar();
Try using ExecuteScalar function. Execute scalar returns a single value and I see you only need the user ID.
Take a look at this link .
int userid = (Int32)com.ExecuteScalar();
I Hope it helps!
Indices in GetInt32 are 0-based as per doc, therefore your call should read:
int userid = reader.GetInt32(0);
Change these lines:
SqlDataReader reader = com.ExecuteReader();
reader.Read();
int userid = reader.GetInt32(1);
to:
var userID = com.ExecuteScalar();
Why:
Execute Scalar should be used when your query returns a single value.
Execute Reader returns a collection of data in the form of a DataReader. DataReaders are fast, and you can quickly iterate over them to get the data you need from the database. The connection remains open as long as the datareader is open.
Because you were only getting a single value back from the database, it makes sense to use ExecuteScalar. It's more efficient and too the point.
If you were getting a list of UserID's, then I'd recommend you use a DataReader to iterate through the UserIDs.
When I use:
START TRANSACTION;
INSERT IGNORE INTO users (Name, ...) VALUES ('user1',..) ON DUPLICATE KEY UPDATE lastSeen=NOW();
SELECT LAST_INSERT_ID();
COMMIT;
When i run this query from my sql client I get the last inserted id as expected.
But what if the user already exists?(table uniquness). So no new id is created. But now when I run the same query from my sql client still i get the id as expected.
But! when I run it from my C# code and use mysql.reader to query the result I receive 0. Why is that??
If instead of SELECT LAST_INSERT_ID() I use SELECT id FROM users ORDER BY id DESC LIMIT 1 I get the right id again.
EDIT!
This is not a duplicate of the suggested topic. In that topic the answer says that the auto-incremented is not the primary key, thats not my case! also as I mark it does return the right id when i use the mysql client! The problem occures only when I run it from my c# code!
My C# code:
using (var conn = new MySqlConnection(connString))
{
try
{
conn.Open();
var command = conn.CreateCommand();
command.CommandText = strSQL;
MySqlDataReader reader;
string result = "";
try
{
reader = command.ExecuteReader();
if (reader.Read())
result = reader[0].ToString();
}
catch (Exception ex)
{
throw new MySqlException("SQL Error: " + ex.ToString());
}
reader.Close();
return result;
}
}
I have found a work around, simple CASE clause does the job:
START TRANSACTION;
INSERT IGNORE INTO users (Name, ...) VALUES ('user1',..) ON DUPLICATE KEY UPDATE lastSeen=NOW();
SELECT CASE WHEN LAST_INSERT_ID()=0 THEN (SELECT id FROM users WHERE Name = 'user1') ELSE LAST_INSERT_ID() END;
COMMIT;
Now in case the user exists, we simply query the id, If it doest not exists, the LAST_INSERT_ID() will give us the right id
From what I read there https://stackoverflow.com/a/15057619/4421474
Your code could be transformed to:
using (var conn = new MySqlConnection(connString))
{
conn.Open();
var command = conn.CreateCommand();
command.CommandText = strSQL; <-- just insert part like "START ... INSERT ... COMMIT;"
command.ExecuteNonQuery();
string result = command.LastInsertedId;
return result;
}
Sorry I am not c# expert so some syntax could be broken.
I guess this answer:
Just my 50 cents for this issue, I simply noticed that you won't get a LAST_INSERT_ID greater than 0 if your table has no AUTO_INCREMENT set to an index
that i found here MySQL: LAST_INSERT_ID() returns 0 is the good one.
I have a C# app with have to use SQL Server database with 1 table (All_Data) and 5 columns (ID, Name, Surename, Age,Location)
Before inserting a new row how can I find out or get the value of the last ID in the table
I have a following code but it,a not work well
string query = "SELECT MAX(ID) FROM All_Data";
SqlCommand comSelect;
comSelect = new SqlCommand(query, connection);
int ID = (int)comSelect.ExecuteScalar();
ERROR message:
ExecuteScalar: Connection property has not been initialized
Please help me
First, from your code it is not clear what is the value of the variable connection.
From the error message it seems that you don't have initialized this variable and thus you get the error. (connection = new SqlConnection(....);)
However, this is not the correct way to handle this scenario.
You need to make the ID column an IDENTITY column and then don't try to retrieve its value before executing any INSERT.
An IDENTITY column receives its value directly from the database when there is a new record to insert. And letting the database code work on this data it is the best option if you want to be safe from concurrency issues.
If you need to retrieve the ID value after an INSERT query because you need it as a Foreign Key in other tables or for your own code, then you could simply use the T-SQL command
SELECT SCOPE_IDENTITY()
For example, suppose you have to insert a record in that table, and you want to know the IDENTITY value assigned to the ID column
string query = #"INSERT INTO All_Data(Name,Surename,Age,Location)
VALUES(#name, #surname, #age, #loc);
SELECT SCOPE_IDENTITY()";
using(SqlConnection connection = new SqlConnection(connectionString))
using(SqlCommand cmd = new SqlCommand(query, connection))
{
connection.Open();
cmd.Parameters.AddWithValue("#name", yourNameValue);
.... other parameters ...
int newID = Convert.ToInt32(cmd.ExecuteScalar());
}
As you can see, this code doesn't try to pass a value for the ID column. It pass just the other fields with a parameterized query. But at the end of the first query there is a call to SELECT SCOPE_IDENTITY() and this returns whatever value the database has assigned to the column ID (of course you should have set the IDENTITY property on the field).
This will work correctly in multiuser and concurrent scenario
The error fires when the command doesn't have a connection. Please check connection is open.
Error saysExecuteScalar: Connectio property has not been initialized
double Check your connection string whether it is defined properly. You can check here to know how to define connection string.
you have not opened connection so open it before use :
comSelect = new SqlCommand(query, connection);
connection.Open();
int ID = (int)comSelect.ExecuteScalar();
connection.Close();
Here is the query:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
";
Now I need this last inserted id back, which is a UUID generated by MySQL. As far as I read there is no select_last_insert_id() function for UUIDs!! And I read for php you could assign UUID() function first to a variable and then return that value. But how to go about that in C#?
Something like this, but not exactly:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (#UUID = SELECT UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
"; //how to do this here?
Here is more of my code:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (#UUID = SELECT UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#LogInTime", DateTime.Now);
cmd.Parameters.AddWithValue("#MIp", GetMachineIP());
cmd.Parameters.AddWithValue("#MFingerPrint", GetHardwareFingerPrint());
var s= Convert.ToString(cmd.ExecuteScalar()); //this returns an empty string :(
//I need to get it to any .NET data type, string, or Guid or byte[] or anything.
But I need this datatype of s to be used in another WHERE clause in a query like this:
string query = #"UPDATE session SET logout_time = #LogOutTime
WHERE user_id = #UId AND PK_Id = #SessionId";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#SessionId", s);
cmd.Parameters.AddWithValue("#LogOutTime", DateTime.Now);
cmd.ExecuteScalar();
Here #"SessionId" is the UUID field in the same table. So basically, how can I get the MySQL varbinary field in C# so that I could use that type to update by specifying WHERE in another query?
In MySQL table the UUID field is varbinary (I hope to see some solution that is not another php link or that is not asking me to switch to char datatype in the database :) ).
Edit: The problem here is we have already added plenty of UUIDs generated by MySQL into the table, so I'm a bit apprehensive about changing MySQL UUID to .NET Guid. If that's the only workaround, I'll consider that. Just that this is the first time we needed the inserted UUID value back so that I can update in another query another point of time.
A sub question: Is .NET Guid exactly the same thing as MySQL UUID?
You can use the Guid type which is the MS implementation of UUID. You should be aware that when inserting data into the DB, you may need to convert the Guid to ByteArray if the MySQL driver isn't familiar with handling Guid's. See Store GUID in MySQL from C# for an example of this.
I think you can go ahead with your earlier implementation without having to rely on MS Guid, but I fear I am too late :)
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
SELECT PK_Id FROM session WHERE login_time=#LogInTime AND machine_fingerprint=#MFingerPrint; //or something similar which gives you the exact same id - UUID
";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#LogInTime", DateTime.Now);
cmd.Parameters.AddWithValue("#MIp", GetMachineIP());
cmd.Parameters.AddWithValue("#MFingerPrint", GetHardwareFingerPrint());
MySqlDataReader r = cmd.ExecuteReader();
if (r.Read()) //ensure if it is read only once, else modify your `WHERE` clause accordingly
{
var s = (Guid)r[0];
}
//or even (Guid)cmd.ExecuteScalar() would work
Now you can query in update like this:
string query = #"UPDATE session SET logout_time = #LogOutTime
WHERE user_id = #UId AND PK_Id = #SessionId";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#SessionId", s.ToByteArray());
cmd.Parameters.AddWithValue("#LogOutTime", DateTime.Now);
cmd.ExecuteNonQuery();
Note: Here I have converted the Guid variable s to byte array before querying. This is important, in WHERE clause, be it UPDATE or SELECT statements in query. I would ask you to move to binary field in MySQL table from varbinary.
Edit: If your table would grow dramatically large then inserting and selecting is a bad idea since SELECT query is an additional query being run. In that case #PinnyM's choice is better. I really do not think MySQL or any other database would have a default way to give back "custom" inserted ids which are not something database generated. So in short I advice you to not go for this..
Edit2: See this answer for getting binary value to .NET datatype. Sometimes casting do not work depending on MySQL .NET connector version..