I am currently working on a C# project and I am running an insert query which also does a select at the same time, e.g.:
INSERT INTO table (SELECT * FROM table WHERE column=date)
Is there a way I can see how many rows were inserted during this query?
ExecuteNonQuery - returns the number of rows affected.
SqlCommand comm;
// other codes
int numberOfRecords = comm.ExecuteNonQuery();
If you run the SQL from your question in a SqlCommand and check the return value of ExecuteNonQuery it should tell you how many records were affected.
From the documentation:
Return Value
Type: System.Int32
The number of rows affected.
Be sure of one thing also
You need to add a statement in the connection string
For example:
string const "Server=localhost; PORT=3306; Database=db; User id=root; password='';UseAffectedRows=True";
MySqlConnection con = new MySqlConnection(const);
con.Open();
MySqlCommand cmd = new MySqlCommand(con);
cmd.CommandText = "Update db set table = value where Column = value";
int numberOfRecords = cmd.ExecuteNonQuery();
Be sure of:
UseAffectedRows=True
so it will return a right value of rows affected
ExecuteNonQuery return the affected rows ONLY WHEN Use Affected Rows in the connections properties is set, if not (default) returns matched rows.
If you run a bulk of ExecuteNonQuery(), and commit them all in once, you can get the number of total changes after connection by read the return value from "SELECT total_changes();"
The function to get the total changes:
public static long GetTotalChanges(SQLiteConnection m_dbConnection)
{
string sql = "SELECT total_changes();";
using (SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection))
{
using (SQLiteDataReader reader = command.ExecuteReader())
{
reader.Read();
return (long)reader[0];
}
}
}
Use it in another function:
public static long MyBulkInserts()
{
using (SQLiteConnection m_dbConnection = new SQLiteConnection())
{
m_dbConnection.Open();
using (var cmd = new SQLiteCommand(m_dbConnection))
{
using (var transaction = m_dbConnection.BeginTransaction())
{
//loop of bulk inserts
{
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
return GetTotalChanges(m_dbConnection);
}
}
I realize you are trying to do this with the ExecuteNonquery, but what about ExecuteScalar and using the OUTPUT directive in your query?
For Insert:
declare #resulttable
(
rowid int
)
insert yourtable
output inserted.rowid
into #resulttable
select *
from someothertable
select count(1) affectedrows
from #resulttable
or for Update, if you only want to know the rows that changed
declare #resulttable
(
beforefield1 varchar(255),
afterfield1 varchar(255)
)
update tbl1
set field1 = replace(field1, 'oldstring', 'newstring')
output deleted.field1,
inserted.field1
into #resulttable
from someothertable
select count(1) affectedrows
from #resulttable
where beforefield1 != afterfield1;
Related
So I have the following code (heavily simplified) -
MySqlCommand CMD = new MySqlCommand(#"INSERT INTO helpdesk.calls Set CallID = #CallID, Viewed = 1, Date = current_timestamp();
SELECT * FROM helpdesk.calls WHERE ID = #CallID", Con);
CMD.Parameters.Add("CallID", MySqlDbType.Int32).Value = ID;
using (MySqlDataReader Reader = CMD.ExecuteReader()) {
if (Reader.Read()) {
RetVal = ToCallHeader(Reader);
} else {
// this never throws as Reader.Read() always returns true.
throw new Exception(string.Format("Call ({0}) not found", ID));
}
}
where I am inserting a record and also running a select at the same time. The problem is that Reader.Read() returns true whether any rows are selected from the select statement or not when an insert statement is used. Is this behavor by design and do I require two queries with a transaction?
That Insert Query syntax is wrong and it will throw an exception.
INSERT INTO helpdesk.calls Set CallID = #CallID, Viewed = 1, Date = current_timestamp();
Check here for the correct sql syntax on insert statement: https://www.w3schools.com/sql/sql_insert.asp
I'm trying to insert a dataset into an SQL database but I am having difficulties passing my dataset as an argument to my DB class. I am not sure if it is allowed to pass as an argument. If not, what are my alternatives?
The way I create my dataset:
public static void getLogs() {
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\someDir";
SQLiteConnection cn = new SQLiteConnection("Data Source=" + path + ";Version=3;New=False;Compress=True;");
cn.Open();
SQLiteDataAdapter sd = new SQLiteDataAdapter("SELECT * FROM table", cn);
DataSet ds = new DataSet();
sd.Fill(ds);
cn.Close();
db.InsertLogs(Form1.adminID, Form1.deviceID, ds);
}
My database class and insert method looks like the following:
public void InsertLogs(string user_id, string device_id, DataSet history)
{
string query = "INSERT INTO table (column1, column2, column3, column4, column5, column6, column7) VALUES (#value1, #value2, #value3, #value4, #value5, #value6, #value7);";
if (OpenConnection() == true)
{
foreach (DataTable table in history.Tables)
{
foreach (DataRow row in table.Rows)
{
MySqlCommand cmd = new MySqlCommand(query, connection);
cmd.Parameters.AddWithValue("#value1", int.Parse(user_id));
cmd.Parameters.AddWithValue("#value2", int.Parse(device_id));
cmd.Parameters.AddWithValue("#value3", row[0]);
cmd.Parameters.AddWithValue("#value4", row[1]);
cmd.Parameters.AddWithValue("#value5", row[2]);
cmd.Parameters.AddWithValue("#value6", row[3]);
cmd.Parameters.AddWithValue("#value7", row[4]);
cmd.ExecuteNonQuery();
}
}
CloseConnection();
}
}
Thank you
you can loop through datatables in a dataset and can pass a datatable as a stored procedure paramater,
found an example here
1.- Go to SQL Server, under your DB name go to "programmability\Types\User-Defined Table Types, right click and create a new one:
USE DBNAME
GO
-- Create the data type
CREATE TYPE ValuesToInsert AS TABLE
(
Value1 INT NOT NULL,
Value2 INT NOT NULL,
Value3 VARCHAR(20)
)
GO
2.- Create a SP to receive the table as parameter, parameter must be the new User-Defined table type created in step 1
USE [DBNAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--
CREATE PROCEDURE [dbo].[spImportData]
#DataImported dbo.ValuesToInsert READONLY
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO [dbo].[TableName] (Value1, Value2, Value3)
SELECT Value1, Value2, Value3
FROM #DataImported
3.- Pass a datatable from your code to DB, in this case using Dapper.net as following:
DataTable dtExcelData = new DataTable();
//Fill dtExcelData and pass as parameter
ParametersCollection param = new ParametersCollection();
param.Add(CreateParameter("#DataImported", dtExcelData));
ExecuteDataSet("spImportData", CommandType.StoredProcedure, param);
I have this code:
SqlConnection cnn = new SqlConnection();
cnn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select * from Szkoda";
cmd.Connection = cnn;
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
DataSet ds = new DataSet();
da.Fill(ds, "Szkoda");
SqlCommandBuilder cb = new SqlCommandBuilder(da);
DataRow drow = ds.Tables["Szkoda"].NewRow();
drow["Likwidator"] = tbLikwidator.Text;
drow["FirmaObslugujaca"] = DdFirma.Text;
drow["StanSzkody"] = DdStan.Text;
drow["CzyRegres"] = DdRegres.Text;
drow["KrajZdarzenia"] = DdKraj.Text;
ds.Tables["Szkoda"].Rows.Add(drow);
da.Update(ds, "Szkoda");
The question is how to get the inserted record ID? I read about scope but I don't know how I can use this in above code.
I want to get last ID to redirect to view form after save new record.
I'm looking for simplest solution:)
You can't do that directly from the Update command of the DataAdapter. You need to prepare a custom insert command that contains two commands. The first insert your record, the second one returns the last inserted id from your connection
string insertText = #"INSERT INTO Szkoda (Likwidator,FirmaObslugujaca,
StanSzkody, CzyRegres, KrajZdarzenia)
values (#lik, #fir, #sta, #czy, #kra);
SELECT SCOPE_IDENTITY()";
SqlCommand cmd = new SqlCommand(insertText, connection);
cmd.Parameters.AddWithValue("#lik", tbLikwidator.Text);
cmd.Parameters.AddWithValue("#fir", DdFirma.Text);
cmd.Parameters.AddWithValue("#sta", DdStan.Text);
cmd.Parameters.AddWithValue("#cay", DdRegres.Text);
cmd.Parameters.AddWithValue("#kra", DdKraj.Text);
object result = cmd.ExecuteScalar();
if(result != null)
{
int lastInsertedID = Convert.ToInt32(result);
// now insert the row in your dataset table but instead of
// da.Update(ds, "Szkoda"); call
ds.Tables["Szkoda"].AcceptChanges();
}
Of course this should go alongside with your existing code, but instead of calling Update just call AcceptChanges to your datatable to confirm the new record in your table
Aftre insert the record into table(using sql query, not stored procedure) from c# code, you can use Get Records function to Select last record id(not recommended, because in muliuser case, this will be wrong) using max() fucntion.
select * from Szkoda where ID IN (select max(id) from Szkoda)
If you are using Stored Procedure to insert data, then Use SCOPE_Identity() in stored procedure, and use Output parameter to get value in c# code.
CREATE PROCEDURE dbo.testSP
#Col1 VARCHAR(50),
#Col2 VARCHAR(20),
#new_identity INT = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.TestTable(Col1, Col2) SELECT #Col1, #Col2;
SET #new_identity = SCOPE_IDENTITY();
END
GO
Refer this Return identity of last inserted row from stored procedure
I searched on the net something but nothing really helped me. I want to update, with a list of article, a database, but the way that I've found is really slow.
This is my code:
List<Article> costs = GetIdCosts(); //here there are 70.000 articles
conn = new OleDbConnection(string.Format(MDB_CONNECTION_STRING, PATH, PSW));
conn.Open();
transaction = conn.BeginTransaction();
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = transaction;
cmd.CommandText = "UPDATE TABLE_RO SET TABLE_RO.COST = ? WHERE TABLE_RO.ID = ?;";
for (int i = 0; i < costs.Count; i++)
{
double cost = costs[i].Cost;
int id = costs[i].Id;
cmd.Parameters.AddWithValue("data", cost);
cmd.Parameters.AddWithValue("id", id);
if (cmd.ExecuteNonQuery() != 1) throw new Exception();
}
}
transaction.Commit();
But this way take a lot of minutes something like 10 minutes or more. There are another way to speed up this updating ? Thanks.
Try modifying your code to this:
List<Article> costs = GetIdCosts(); //here there are 70.000 articles
// Setup and open the database connection
conn = new OleDbConnection(string.Format(MDB_CONNECTION_STRING, PATH, PSW));
conn.Open();
// Setup a command
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE TABLE_RO SET TABLE_RO.COST = ? WHERE TABLE_RO.ID = ?;";
// Setup the paramaters and prepare the command to be executed
cmd.Parameters.Add("?", OleDbType.Currency, 255);
cmd.Parameters.Add("?", OleDbType.Integer, 8); // Assuming you ID is never longer than 8 digits
cmd.Prepare();
OleDbTransaction transaction = conn.BeginTransaction();
cmd.Transaction = transaction;
// Start the loop
for (int i = 0; i < costs.Count; i++)
{
cmd.Parameters[0].Value = costs[i].Cost;
cmd.Parameters[1].Value = costs[i].Id;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// handle any exception here
}
}
transaction.Commit();
conn.Close();
The cmd.Prepare method will speed things up since it creates a compiled version of the command on the data source.
Small change option:
Using StringBuilder and string.Format construct one big command text.
var sb = new StringBuilder();
for(....){
sb.AppendLine(string.Format("UPDATE TABLE_RO SET TABLE_RO.COST = '{0}' WHERE TABLE_RO.ID = '{1}';",cost, id));
}
Even faster option:
As in first example construct a sql but this time make it look (in result) like:
-- declaring table variable
declare table #data (id int primary key, cost decimal(10,8))
-- insert union selected variables into the table
insert into #data
select 1121 as id, 10.23 as cost
union select 1122 as id, 58.43 as cost
union select ...
-- update TABLE_RO using update join syntax where inner join data
-- and copy value from column in #data to column in TABLE_RO
update dest
set dest.cost = source.cost
from TABLE_RO dest
inner join #data source on dest.id = source.id
This is the fastest you can get without using bulk inserts.
Performing mass-updates with Ado.net and OleDb is painfully slow. If possible, you could consider performing the update via DAO. Just add the reference to the DAO-Library (COM-Object) and use something like the following code (caution -> untested):
// Import Reference to "Microsoft DAO 3.6 Object Library" (COM)
string TargetDBPath = "insert Path to .mdb file here";
DAO.DBEngine dbEngine = new DAO.DBEngine();
DAO.Database daodb = dbEngine.OpenDatabase(TargetDBPath, false, false, "MS Access;pwd="+"insert your db password here (if you have any)");
DAO.Recordset rs = daodb.OpenRecordset("insert target Table name here", DAO.RecordsetTypeEnum.dbOpenDynaset);
if (rs.RecordCount > 0)
{
rs.MoveFirst();
while (!rs.EOF)
{
// Load id of row
int rowid = rs.Fields["Id"].Value;
// Iterate List to find entry with matching ID
for (int i = 0; i < costs.Count; i++)
{
double cost = costs[i].Cost;
int id = costs[i].Id;
if (rowid == id)
{
// Save changed values
rs.Edit();
rs.Fields["Id"].Value = cost;
rs.Update();
}
}
rs.MoveNext();
}
}
rs.Close();
Note the fact that we are doing a full table scan here. But, unless the total number of records in the table is many orders of magnitude bigger than the number of updated records, it should still outperform the Ado.net approach significantly...
I am going to have multiple processes running at the same time so what I tried to do here is fetch 1000 rows and then update the rows i selected.. below are my Select and Update functions notice i call the update function right after closing the connection in the select function
public List<string> Select()
{
string set;
string query = "SELECT * FROM master WHERE attempted='0' LIMIT 1000";
List<string> list = new List<string>();
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (list.Count() < 1000)
{
dataReader.Read();
string email = dataReader["email"].ToString();
var m = dataReader["attempted"];
if (m.ToString() == "0")
{
list.Add(email);
}
}
dataReader.Close();
this.CloseConnection();
Update();
return list;
}
else
{
return list;
}
}
public void Update()
{
if (this.OpenConnection() == true)
{
string query = "UPDATE master SET attempted='1' WHERE ( SELECT * FROM master WHERE attempted='0' LIMIT 1000 )";
MySqlCommand cmd = new MySqlCommand(query, connection);
cmd.CommandText = query;
cmd.ExecuteNonQuery();
this.CloseConnection();
}
}
the exception i am getting it operand must contain 1 column(s)..
What am I doing wrong?
Why can't you just create a separate column, or even a table? Then write a basic Query at the SQL level or a Procedure to modify the value? Then the other applications can just test the value of the column or table.
Example:
UPDATE [dbo].[Customer]
SET [GotEmail] = 1
WHERE (
SELECT [Email]
FROM [dbo].[Customer]
);
Or something basic like that? Another example would be:
UPDATE accounts
SET (contact_last_name, contact_first_name) =
(SELECT last_name, first_name FROM salesmen
WHERE salesmen.id = accounts.sales_id);
Does a simple query like so not solve your issue?
If my response is not clear... I'll try and clarify my thought process.