I have looked around and I am still confused on how to get the last inserted ID. I added the statment SELECT LAST_INSERT_ID(); at the end of mysql statement i am executing. I am storing the value in prID = Convert.ToInt32(cmd.ExecuteScalar()); This command is creating two instances in my database. I am pretty sure I need to separate these two statements but unsure how to while still getting the last ID.
try
{
Console.WriteLine("Connecting to MySQL...");
conn.Open();
string sql = "INSERT INTO pull_requests (repoID, branchID, fileID, prStatus, prComments) VALUES (#rID, #bID, #fID, #prS, #prC); SELECT LAST_INSERT_ID();";
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#rID", RI);
cmd.Parameters.AddWithValue("#bID", BI);
cmd.Parameters.AddWithValue("#fID", FI);
cmd.Parameters.AddWithValue("#prS", 0);
cmd.Parameters.AddWithValue("#prC", comment);
prID = Convert.ToInt32(cmd.ExecuteScalar());
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
conn.Close();
You need to only call cmd.ExecuteNonQuery() to execute the insert statement. On the return, the cmd object will have its .LastInsertedId property populated for you.
Like this:
try
{
Console.WriteLine("Connecting to MySQL...");
conn.Open();
string sql = "INSERT INTO pull_requests (repoID, branchID, fileID, prStatus, prComments) VALUES (#rID, #bID, #fID, #prS, #prC);";
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#rID", RI);
cmd.Parameters.AddWithValue("#bID", BI);
cmd.Parameters.AddWithValue("#fID", FI);
cmd.Parameters.AddWithValue("#prS", 0);
cmd.Parameters.AddWithValue("#prC", comment);
cmd.ExecuteNonQuery();
long lastId = cmd.LastInsertedId;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
conn.Close();
Use the MySqlCommand.LastInsertedId property after executing the query.
Typically, the SELECT last_insert_id() is executed separately, immediately after the INSERT is executed, on the same connection. The result of last_insert_id() is connection specific, so you do not need to worry about other clients "overwriting" yours.
You can even reuse the same command with just cmd.CommandText = "SELECT last_insert_id()";
...but as others have pointed out, and a quick web search has clarified for me, it looks like the MySQL .Net connector you are using already provides that without a second query.
Related
I have a routine where I update the local database with other database data.
I only execute a DELETE and then an INSERT INTO tblX (SELECT * FROM tblY (tblY is a linked table)), as below.
The problem is that, in some cases the SELECT takes a long time after the DELETE and I´d like to diminish the possibility of the user to make a request to this table while it´s processing.
I´d like to know if there is some mechanism to execute the DELETE only after the return of the SELECT.
conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal());
conn.Open();
OleDbCommand cmd = new OleDbCommand(" DELETE * FROM tblClienteContato; ", conn);
cmd.ExecuteNonQuery();
cmd = new OleDbCommand(" INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;", conn);
cmd.ExecuteNonQuery();
It sounds like what you need to do is wrap both of those commands in a transaction.
The cool thing about a transaction is that it either ALL WORKS or ALL FAILS, meaning that if something happens to stop the select statement, the database will not finalise the delete statement.
This looks like a really good example to work with:
https://msdn.microsoft.com/en-us/library/93ehy0z8(v=vs.110).aspx
Note that they have one command object, and replace the CommandText, rather than create a new object each time. This is probably important.
Try something like this:
conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal());
OleDbCommand cmd = new OleDbCommand();
OleDbTransaction transaction = null;
try {
conn.Open();
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
cmd.Connection = conn;
cmd.Transaction = transaction;
cmd.CommandText = " DELETE * FROM tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = " INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
// The data isn't _finally_ completed until this happens
transaction.Commit();
}
catch (Exception ex)
{
// Something has gone wrong.
// do whatever error messaging you do
Console.WriteLine(ex.Message);
try
{
// Attempt to roll back the transaction.
// this means your records won't be deleted
transaction.Rollback();
}
catch
{
// Do nothing here; transaction is not active.
}
}
You should look into BeginTransaction, Commit and rollback, here's an example:
_con.Open();
_con_trans = _con.BeginTransaction();
using(SqlCommand cmd = _con.CreateCommand())
{
cmd.CommandText = "delete from XXXXX";
cmd.CommandType = CommandType.Text;
cmd.Transaction = _con_trans;
cmd.ExecuteNonquery();
}
using(SqlCommand cmd = _con.CreateCommand())
{
cmd.CommandText = "insert into XXXX";
cmd.CommandType = CommandType.Text;
cmd.Transaction = _con_trans;
cmd.ExecuteNonquery();
}
_con_trans.Commit();
_con_trans = null;
_con.Close();
This way, everything is wrapped under a single transaction, so when the delete begins, the table will be locked for reading and writing.
Without knowing the schema of the table, it is hard to identify why the delete process is taking an extended amount of time.
An alternative to wrapping the commands within a transaction would be to simply delete the table itself rather than the data within it by using the DROP TABLE command. And then you can recreate the table utilizing the SELECT...INTO...FROM statement to recreate. A potential advantage to this is that the schemas will match identically, and any inherent conversions (eg decimal to int) will not need to be done.
using (conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal())) {
conn.Open();
using (OleDbCommand cmd = new OleDbCommand()) {
cmd.CommandText = "DROP TABLE tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * INTO tblClienteContato FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
}
}
The following does not apply here (MS Access), but may to other SQL variants
Another option is to utilize the TRUNCATE command, which will delete everything in the table in one fell swoop. There is no logging of the individual rows and the indexes (if present) don't need to be recalculated on each and every line being deleted. The catch to this method is that this will not work within the transaction. If there is an Identity column the value will be reset as well. There are other potential cons to this but without knowing the design of the table I have no way of identifying them.
using (conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal())) {
conn.Open();
using (OleDbCommand cmd = new OleDbCommand()) {
cmd.CommandText = "TRUNCATE TABLE tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = " INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
}
}
As Greg commented, I created temporary tables to receive data from the external database and then I tranfer the data to the definitive tables, so that the probability of the users being impacted is very low.
string sql = "Insert into tbl_borrowed (FirstName,LastName,BookName,Category,DateBorrowed,Time,DateToBeReturned) values (#fname,#lname,#bname,#category,#dborrow,#time,#dreturn)";
string sql2= "Insert into tbl_return (FirstName,LastName,BookName,Category,DateBorrowed,Time) values (#fname,#lname,#bname,#category,#dborrow,#time";
MySqlCommand sda = new MySqlCommand("", conn);
sda.CommandType = CommandType.Text;
sda.CommandText = sql;
sda.Parameters.AddWithValue("#fname", txtfname.Text);
sda.Parameters.AddWithValue("#lname", txtlname.Text);
sda.Parameters.AddWithValue("#bname", txtbook.Text);
sda.Parameters.AddWithValue("#category", cmbcategory.Text);
sda.Parameters.AddWithValue("#dborrow", dateTimePicker1.Value.Date);
sda.Parameters.AddWithValue("#time", this.time.Text);
sda.Parameters.AddWithValue("#dreturn", dateTimePicker2.Value.Date);
MessageBox.Show("Item has been added!");
showlv("Select * from tbl_borrowed", lvborrowed);
showlv2("Select * from tbl_return", rb.lvreturn);
txtfname.Clear();
txtlname.Clear();
txtbook.Clear();
cmbcategory.Clear();
dateTimePicker1.ResetText();
dateTimePicker2.ResetText();
try
{
conn.Open();
sda.CommandText = sql2;
sda.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show("ASDF" + ex);
}
finally
{
conn.Close();
}
I want to insert same values into 2 tables. Im just a beginner, please help me. Bear with me...
ERROR:
In your INSERT query Time is a reserved word and needs to be escaped using backtique like below. BTW, both of your INSERT statement have the same mistake.
Insert into tbl_borrowed (FirstName,LastName,BookName,Category,DateBorrowed,`Time`,DateToBeReturned)
Again, instead of executing multiple INSERT that way it's much better you wrap those queries in a stored procedure and call that procedure from your code. That's way if you needed you can actually have both the INSERT running in the same transaction block by wrapping both of them in a begin trans block.
I have been trying to find a solution to this problem but so far nothing worked.
private void Insert()
{
string ConnectionStringAccess = Provider=Microsoft.ACE.OLEDB.12.0;Data Source=###Jet OLEDB:Database Password=###;
string query2 = "Select ##Identity";
int id = -1;
string Query = "INSERT INTO tblTable (EmpNo, Name) VALUES (132, 'TestName');";
OleDbConnection con = new OleDbConnection(ConnectionStringAccess);
OleDbCommand cmd = new OleDbCommand(Query, con);
try
{
con.Open();
if (cmd.ExecuteNonQuery() == 1)//the insert succeded
{
cmd.CommandText = query2;
id = Convert.ToInt32(cmd.ExecuteScalar());
}
}
catch (Exception ex)
{
//log the ex
}
finally
{
con.Dispose();
con.Close();
}
}
Each time I use the above method I always get a return of 0 in "id". What am I doing wrong? I tried using a different connection string or another way to get latest identifier:
Provider=Microsoft.Jet.OLEDB.4.0;
SCOPE_IDENTITY()
but again nothing. The Access db is 2003 or older (not sure exactly).
The ms access db is 2003 or older (not sure exactly)
I was able to recreate your issue with an Access 97 database. SELECT ##IDENTITY worked correctly with an Access 2000 database file (even when run from the same OleDbCommand object as the INSERT), but it always returned zero when run against an Access 97 database.
It appears that you will need to upgrade your database file to a newer version if you want SELECT ##IDENTITY to work.
You are using the same command object for both the insert and retrieval of ##identity.
According to this article you should create a separate command object for retrieving the ##identity value:
http://support.microsoft.com/kb/815629
Also, just to verify, the table you are inserting to does have an auto increment column, is that correct? If not, ##identity would not return anything.
Create two different commands for your queries, execute non query then execute scalar. It will return the first column of the first row in the result set returned by the query and it should be the id you're looking for.
private void Insert()
{
string ConnectionStringAccess = Provider=Microsoft.ACE.OLEDB.12.0;Data Source=###Jet OLEDB:Database Password=###;
int id = -1;
string Query = "INSERT INTO tblTable (EmpNo, Name) VALUES (132, 'TestName')";
string Query2 = "SELECT ##Identity";
OleDbConnection con = new OleDbConnection(ConnectionStringAccess);
OleDbCommand cmd = new OleDbCommand(Query, con);
OleDbCommand cmd2 = new OleDbCommand(Query2, con);
try
{
con.Open();
cmd.ExecuteNonQuery();
id = (int)cmd2.ExecuteScalar();
}
catch (Exception ex)
{
//log the ex
}
finally
{
con.Dispose();
con.Close();
}
}
thanks for all the responses. I found out what the problem was. Apparently the access file is very old, 1997 to be exact and that was the problem. As soon as a tried a new access 2010 file it worked.
Thanks again
My soluce with my very older databases (VB6 and ACCESS)
With VB NET and before upgrade Database to 4.
'MyInsertCommand.CommandText = "Select ##Identity" Don't work with old Access database
MyInsertCommand.CommandText = "SELECT TOP 1 ME_idn FROM MESURE ORDER BY ME_idn Desc"
Dim MyInsertIdn As Integer = MyInsertCommand.ExecuteScalar()
I am trying to execute a stored procedure through C#, ADO.NET and below is the code I am trying to execute:
using (SqlConnection conn = new SqlConnection(".;Initial Catalog=MyDB;User ID=sa;Password=***"))
{
try
{
string cmdText = "dbo.sp_Create_FlaggedItemEntry #URI, #ID";
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
conn.Open();
cmd.CommandText = cmdText;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#URI", value1);
cmd.Parameters.AddWithValue("#ID", value2);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (conn != null)
{
conn.Close();
}
}
}
Now when I try to debug it, I got an error at the line - cmd.ExecuteNonQuery(); - "Could Not Find Stored Procedure dbo.sp_Create_FlaggedItemEntry"
I verified that the Connection String is all correct and Stored Procedure exists.
Further, If I change the line - cmd.CommandType = CommandType.StoredProcedure; to cmd.CommandType = CommandType.Text; it get executed successfully and as expected.
Can someone suggest what I am missing and doing wrong here - Please pardon me if it is something very basic as it is quite long since I last worked with ADO.NET
CommandType.StoredProcedure means that the CommandText should only contain the name of the stored procedure.
Remove the parameter names from the string.
Take the parameters out of the command text. Also, you don't need to specify dbo.
The reason it's working with CommandType.Text is because it's a legitimate SQL command like that - if you were to open up SSMS and type that in it'd work as long as you also create the variables #URI and #ID
Documentation here
You should mention Data Source / Server in connectionString. Also for CommandText #Slaks is correct.
i have a question if you please help me i have an error
Must declare the scalar variable
"#Deitails".
and i can not find out whats the problem since i am not aware what Scalar is about
var sqlCon = new
SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
// GET CONFERENCE ROLE ID
SqlCommand cmd = new SqlCommand();
cmd.Connection = sqlCon;
cmd.CommandText = "select Conference_Role_ID from AuthorPaper
where Paper_ID = #PaperId";
cmd.Parameters.AddWithValue("#PaperId",
paperId);
cmd.Connection.Open();
string ConferenceRoleId = cmd.ExecuteScalar().ToString();
cmd.Connection.Close();
cmd.Dispose();
string query2 = #"insert into
ReviewPaper(Overall_Rating,Paper_id,Conference_role_id,Deitails)
values(0,#paperId,#ConferenceRoleId,#Deitails);select
SCOPE_IDENTITY() as RPID";
cmd = new SqlCommand(query2, sqlCon);
cmd.Parameters.AddWithValue("#paperId",
paperId);
cmd.Parameters.AddWithValue("#ConferenceRoleId",
ConferenceRoleId);
string ReviewPaperId;
try
{
cmd.Connection.Open();
ReviewPaperId = cmd.ExecuteScalar().ToString();
cmd.Connection.Close();
}
catch (Exception ee) { throw ee; }
finally { cmd.Dispose(); }
thanks
You have a SQL query with a parameter named Details, but you forgot to add the parameter.
You have a line of code which says
string query2 = #"insert into ReviewPaper(Overall_Rating, Paper_id,
Conference_role_id, Deitails) values (0,#paperId,#ConferenceRoleId,#Deitails);
select SCOPE_IDENTITY() as RPID";
You provide the parameters #paperId, #ConferenceRoleId and #Deitails for the values for the insert statement. Later you specify the value for the first two parameters, but not #Deitails:
cmd.Parameters.AddWithValue("#paperId", paperId);
cmd.Parameters.AddWithValue("#ConferenceRoleId", ConferenceRoleId);
You need to add a similar line to add the value for #Deitails so that SQL server knows what to do with it. The error you are getting is coming from SQL server because by not adding a value for #Deitails in your C# code, it is not being declared for you in the SQL code which is sent to the server.
To answer your other question, 'Scalar' in this case means that the variable #Deitails represents a single value.