Execute DELETE only when the SELECT returns - c#

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.

Related

Can we execute multiple insert statements into MS Access DB 2016 using OLEDB without closing connection [duplicate]

This question already has answers here:
Inserting Multiple Records into SQL Server database using for loop
(5 answers)
Closed 6 months ago.
can we execute multiple insert statements into MS Access DB 2016 using OLEDB without closing connection/Keeping session alive? I tried moving the OleDB command instantiation, con.open() and con.close() outside of foreach loop but it does not work. Any suggestions?
foreach (var list in lsobj)
{
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
con.Open();
cmd.CommandText = "INSERT INTO tbl(file_dt, name, created_dt)";
"VALUES (#file_dt,#name,#inserted_dt)";
cmd.Parameters.AddWithValue("#file_dt", list.file_dt.ToString());
cmd.Parameters.AddWithValue("#name", list.name);
cmd.Parameters.AddWithValue("#inserted_dt", list.inserted_dt.ToString());
cmd.ExecuteNonQuery();
con.Close();
}
Yes, you can. As a general rule, before .net days, it was quite much the "norm" to open a connection, and keep it open. However, .net system tends to cache and re-use the connections you made, even when closed.
but, for the most part, yes, you can execute multiple statements, and do them with the same connection.
So, say this code example:
void TestFun()
{
using (OleDbConnection conn = new OleDbConnection(Properties.Settings.Default.AccessDB))
{
using (OleDbCommand cmdSQL = new OleDbCommand("", conn))
{
conn.Open();
// save picture as bytes to DB
byte[] fData = File.ReadAllBytes(txtFile.Text);
string strSQL =
#"INSERT INTO MyPictures (FileName, PictureData)
VALUES(#File, #Data)";
cmdSQL.CommandText = strSQL;
cmdSQL.Parameters.Add("#File", OleDbType.VarWChar).Value = txtFileName.Text;
cmdSQL.Parameters.Add("#Data", OleDbType.Binary).Value = fData;
cmdSQL.ExecuteNonQuery();
// display data in our grid
cmdSQL.CommandText = "SELECT ID, FileName FROM MyPIctures";
cmdSQL.Parameters.Clear();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
dataGridView1.DataSource = rstData;
// do more commands - still same connection
}
}
}
Now, you can't execute multiple SQL statements in one "go" like you can with SQL server (just separate several statements by a ";".
However, you can certain create one connection (and even one command object), and re-use it over and over.
And since the connection and cmdSQL object are inside of a using block, then both objects, and including your connection will be closed and tidy up after you are done.
FYI: USE Parameters . Add, NOT add with value.
Now, in your case? Since it is the same command over and over - but ONLY the parameters change?
Then this (air code warning)
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
con.Open();
cmd.CommandText = #"INSERT INTO tbl (file_dt, name, created_dt)
VALUES (#file_dt,#name,#inserted_dt)";
cmd.Parameters.Add("#file_dt", OleDbType.VarWChar);
cmd.Parameters.Add("#name", OleDbType.VarWChar);
cmd.Parameters.Add("#inserted_dt", OleDbType.VarWChar);
foreach (var list in lsobj)
{
cmd.Parameters["#file_dt"].Value = list.file_dt.ToString();
cmd.Parameters["#name"].Value = list.name;
cmd.Parameters["#inserted_dt"].Value = list.inserted_dt.ToString());
cmd.ExecuteNonQuery();
}
con.Close();
Now, I always wrap above in a using block. But, as above again shows, we are free to create a command object, and use it for "many" commands as per first example. and in the 2nd example, we ONLY have to setup the parameters one time, and then use the same connection, same command object over and over.

How to get LAST_INSERT_ID();

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.

Inserting data into a MS Access database

try
{
OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;");
myConnection.Open();
// Create Oledb command to execute particular query
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = myConnection;
// Query to create table with specified data columne
myCommand.CommandText = "CREATE TABLE UXZona([IDZona] int, [Morada] text)";
//myCommand.ExecuteNonQuery();
MessageBox.Show("Tabela criada");
}
catch
{
OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;");
myConnection.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO UXZona (IDZona, Morada) VALUES ('" +
transaction.UnloadPlaceAddress.AddressID + "','" +
transaction.UnloadPlaceAddress.AddressLine2 + "')";
cmd.ExecuteNonQuery();
MessageBox.Show("Dados inseridos");
}
I need to insert data into the database but it isn't working. I launch the program and there are no errors, I do everything but when I check the database the table is empty.
UPDATE
Now when i launch the program I have this error:
"System.InvalidOperationException: 'ExecuteNonQuery: Connection property has not been initialized." on cmd.ExecuteNonQuery();
There are a number of things wrong! I give below corrected code:
try
{
bool success = false;
using (var myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;"))
{
// Create Oledb command to execute particular query
using (var myCommand = new OleDbCommand())
{
myCommand.Connection = myConnection;
// Query to create table with specified data columne
//myCommand.CommandText = "CREATE TABLE UXZona([IDZona] int, [Morada] text)";
//myCommand.ExecuteNonQuery();
//MessageBox.Show("Tabela criada");
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO UXZona (IDZona, Morada) VALUES (#id, #morada)";
var param = cmd.CreateParameter();
param.ParameterName = "#id";
param.OleDbType = OleDbType.Integer;
param.Value = transaction.UnloadPlaceAddress.AddressID;
cmd.Parameters.Add(param);
param = cmd.CreateParameter();
param.ParameterName = "#morada";
param.OleDbType = OleDbType.VarChar;
param.Value = transaction.UnloadPlaceAddress.AddressLine2;
cmd.Parameters.Add(param);
myConnection.Open();
if (cmd.ExecuteNonQuery() == 1)
{
success = true;
}
}
}
if (success)
{
MessageBox.Show("Dados inseridos");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
By way of explanation. I have commented out (but not deleted) all references to creating the table. Table creation and table insertion should be in two different routines. Normally you only create a table once, but insert is probably called many times.
I have placed the OleDbConnection and OleDbCommand within using loops. This is good practice, as they both implement IDisposable. Writing your code like this means that the Garbage Collector (GC) knows immediately that it can safely dispose of the objects after use.
I have changed the insert statement such that it takes parameters. This is highly recommended practice to safeguard against SQL Injection (if you do not know what this is please Google it). In fact Access is relatively immune from the worst forms of SQL Injection, because it rejects any command that contains multiple statements, but please get into good habits. With time you will progress to other databases which do not have this restriction.
I deliberately wait before opening the connection until just before it is needed. Connections consume resources, so it is good practice to use them as sparingly as possible. Also for this reason, I have moved your success message outside of the using loops. This means that the cleanup of resources is not waiting for the user to click OK in the message box.
Finally try catch is all well and good, but normally you want to know why the error occurred. Hence you add (Exception ex) to catch so that you can find the reason.
PS What I forgot to mention. In your original INSERT, you were surrounding both VALUES with single quotes. Only use single quotes for strings/text. Integers and other numbers require no quotes. If you quote them, the database will treat it as a string and you will get a data type error.

Insert into 2 tables at the same time c#

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.

Clear a database table

I'm using the following code to clear a database table:
public void ClearAll()
{
SqlCommand info = new SqlCommand();
info.Connection = con;
info.CommandType = CommandType.Text;
info.CommandText = "edit_.Clear()";
}
Why does it not work?
With a sql command you usually pass a TSQL statement to execute. Try something more like,
SqlConnection con = new SqlConnection(ConfigurationSettings.AppSettings["con"]);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "DELETE FROM Edit_ ";
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
You need to execute the command, so info.Execute() or info.ExecuteNonQuery().
Try info.CommandText='DELETE FROM edit_';
The CommandText attribute is the TSQL statement(s) that are run.
You also need a info.ExecuteNonQuery();
1) Decide whether to use a TRUNCATE or a DELETE statement
Use TRUNCATE to reset the table with all its records and indexes:
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "TRUNCATE TABLE [dbo].[Edit_]";
command.ExecuteNonQuery();
}
Use DELETE to delete all records but do not reset identity/auto increment columns
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "DELETE FROM [dbo].[Edit_]";
command.ExecuteNonQuery();
}
Note that there is another line in the samples. In the sample you provided the SQL statement never gets executed until you call one of the ExecuteXXX() methods like ExecuteNonQuery().
2) Make sure you use the correct object (are you sure its called edit_?). I recommend to put the schema before the table name as in the examples before.
3) Make sure you use the correct connection string. Maybe everything worked fine on the production environment ;-)

Categories

Resources