I need to change my field QB_STATUS from value R to value C. I am doing this in a loop because i cannot "requery" the table as data may have changed.
I have built a list of entries to update. The code does not error and iterates through 5 times (correct based on my idInvoices list) but the field does not get updated.
for (int i = 0; i < idInvoices.Count; i++)
{
// following command will update one row as ID_Invoice is primary key.
// ID_Invoice taken from list previously built in ReadDataToNAVArray
SqlCommand cmd = new SqlCommand("UPDATE tblINVOICES SET QB_STATUS=#Status WHERE ID_INVOICE = #IDInvoice", myConnection);
cmd.Parameters.Add("#Status", "C");
cmd.Parameters.Add("#IDInvoice", idInvoices[i]);
cmd.Dispose();
}
First, you have to execute your query: ExecuteNonQuery; second - do not create command, parameters etc within the loop, just assign values and execute:
// Make SQL readable
String sql =
#"UPDATE tblINVOICES
SET QB_STATUS = #Status
WHERE ID_INVOICE = #IDInvoice";
// wrap IDisposable into "using"
// do not recreate command in the loop - create it once
using (SqlCommand cmd = new SqlCommand(sql, myConnection)) {
cmd.Parameters.Add("#Status", SqlDbType.VarChar); //TODO: check types, please
cmd.Parameters.Add("#IDInvoice", SqlDbType.Decimal); //TODO: check types, please
// Assign parameters with their values and execute
for (int i = 0; i < idInvoices.Count; i++) {
cmd.Parameters["#Status"].Value = "C";
cmd.Parameters["#IDInvoice"].Value = idInvoices[i];
cmd.ExecuteNonQuery();
}
}
You are missing the ExecuteNonQuery in your command.
for (int i = 0; i < idInvoices.Count; i++)
{
SqlCommand cmd = new SqlCommand("UPDATE tblINVOICES SET QB_STATUS=#Status WHERE ID_INVOICE = #IDInvoice", myConnection);
cmd.Parameters.Add("#Status", "C");
cmd.Parameters.Add("#IDInvoice", idInvoices[i]);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
I think you're missing cmd.ExecuteNonQuery();.
An example for a different way of using sql commands:
SqlConnection addConn = new SqlConnection();
addConn.ConnectionString = Properties.Settings.Default.yourDataBaseConnection;
addConn.Open();
SqlCommand addComm = new SqlCommand();
addComm.Connection = addConn;
addComm.CommandText = "sql command";
addComm.ExecuteNonQuery();
Related
I have a table with some columns like
now I want to use a for loop to set
out_0 = 0,
out_1 = 1,
out_2 = 2,
out_3 = 3,
out_4 = 4
so I update it with such code as
string sql = "update exchange_out set #column = #id where member_id = 6;";
SqlCommand cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#column", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
int n = 0;
for (int i = 0; i < 5; i++)
{
cmd.Parameters["#column"].Value = "out_" + i;
cmd.Parameters["#gid"].Value = i;
n = cmd.ExecuteNonQuery();
MessageBox.Show("" + n);
}
but it didn't write any data into the table while it literally did five times of updating, because the messagebox returns "1" five times.
finally I solve this by
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set {0} = {1} where member_id = 6", "out_" + i, i);
}
but I'm still wondering why it didn't work by adding parameters?
any respond will be appreciated. :)
I'm still wondering why it didn't work by adding parameters?
Identifiers such as table and column names cannot be parameterized in this way, only data. Your attempt effectively runs a query like this:
update exchange_out set 'out_1' = 1 where member_id = 6;
It's the same in any programming language:
var data1 = "hello";
var whichData = "1";
Console.WriteLine(data+whichData); //it doesn't compile; you cannot programmatically build a variable name `data1` in this way
The way you found is reasonably the only way but you should still parameterize the data:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set out_{0} = #data where member_id = #id", i);
cmd.CommandText = sql;
cmd.Parameters["#data"].Value = ...
cmd.Parameters["#id].Value = 6;
...
You could also start with an SQL stub like "UPDATE t SET " and repeatedly concatenate on identifiers and parameters:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
var sql = "UPDATE exchange_out SET ";
for (int i = 0; i < 5; i++){
sql += string.Format("out_{0} = #data{0},", i);
cmd.Parameters["#data"+i].Value = ...
}
sql = sql.TrimEnd(',');
sql += " where member_id = #id";
cmd.Parameters["#id"].Value = 6;
cmd.CommandText = sql;
...
This does the update in one operation, running a query like UPDATE t SET out_1 = #data1, out_2 = #data2 ...
These are safe from SQL injection because your code controls the entire SQL; there isn't any capacity for a user to provide '; DROP TABLE Students;-- as the {0} going into the identifier in this case but take care that you don't arrange for it to be possible (don't let the user provide identifier text)..
Your non-parameter attempt is also safe from SQL injection in this case by virtue of inserting intergers that you control, rather than strings you don't, but be careful you don't universally apply the technique and one day include user-suppied strings. If you do find yourself in that suitable you should use something like a whitelist of user input - any string identifier provided by the user that isn't whitelisted should not be put in the SQL
I am trying to use Arraybinding to load multiple rows into a table. The approach works for inline queries but not when done via Stored Procedure.
I am expecting that an exception is raised with inner Errors object populated with row numbers where it failed.
This is an example from Oracle site and it works perfectly fine for me. But as soon as i replace the inline query with a stored procedure, it fails with single error and doesn't contain any information for the row which failed.
https://docs.oracle.com/html/B14164_01/featOraCommand.htm#i1007888
/* Database Setup
drop table depttest;
create table depttest(deptno number(2));
*/
This is the program
using System;
using System.Data;
using Oracle.DataAccess.Client;
class ArrayBindExceptionSample
{
static void Main()
{
OracleConnection con = new OracleConnection();
con.ConnectionString = "User Id=scott;Password=tiger;Data Source=oracle;";
con.Open();
OracleCommand cmd = new OracleCommand();
// Start a transaction
OracleTransaction txn = con.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
int[] myArrayDeptNo = new int[3] { 10, 200000, 30 };
// int[] myArrayDeptNo = new int[3]{ 10,20,30};
// Set the command text on an OracleCommand object
cmd.CommandText = "insert into depttest(deptno) values (:deptno)";
cmd.Connection = con;
// Set the ArrayBindCount to indicate the number of values
cmd.ArrayBindCount = 3;
// Create a parameter for the array operations
OracleParameter prm = new OracleParameter("deptno", OracleDbType.Int32);
prm.Direction = ParameterDirection.Input;
prm.Value = myArrayDeptNo;
// Add the parameter to the parameter collection
cmd.Parameters.Add(prm);
// Execute the command
cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
Console.WriteLine("OracleException {0} occured", e.Message);
if (e.Number == 24381)
for (int i = 0; i < e.Errors.Count; i++)
Console.WriteLine("Array Bind Error {0} occured at Row Number {1}",
e.Errors[i].Message, e.Errors[i].ArrayBindIndex);
txn.Commit();
}
cmd.Parameters.Clear();
cmd.CommandText = "select count(*) from depttest";
decimal rows = (decimal)cmd.ExecuteScalar();
Console.WriteLine("{0} row have been inserted", rows);
con.Close();
con.Dispose();
}
}
Replace the code with stored procedure
cmd.CommandText = "SPName";
cmd.CommandType = CommandType.StoredProcedure;
Stored Procedure
PROCEDURE trial (p_deptno IN number)
AS
BEGIN
insert into ses.depttest(deptno) values (P_deptno);
END trial;
So I've been trying to use a datagridview to add new rows into my database. I would prefer not to load the entire database into the grid beforehand and refresh it. I just want to add the values from the datagrid into the table. I can't figure out a workaround. If I do it normally it will give me the error that all variables need to be unique. I have tried to add the sqlconnection.parameters.clear() function but then it seems that it makes all the values go empty and gives me an error that I am not supplying the neccessary information for the parameters.
string TicketTableInsertString = "INSERT INTO ticketTable (TicketID, num1, num2,num3,num4,num5) VALUES (#TicketID,#num1,#num2,#num3,#num4,#num5)";
SqlConnection sqlConn;
connString = "Data Source=DESKTOP-30191JE\\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
sqlConn = new SqlConnection(connString);
try
{
sqlConn.Open();
int ticketID=0;
SqlCommand selectTicketIDCMD = new SqlCommand(selectTicketIDString, sqlConn);
SqlCommand mainTableInsertCMD = new SqlCommand(MainTableInsertString, sqlConn);
SqlCommand ticketTableInsertCMD = new SqlCommand(TicketTableInsertString, sqlConn);
ticketID = (int)selectTicketIDCMD.ExecuteScalar()+1;
sqlConn.Close();
for (int i = 0; i < dgv_ticket.Rows.Count; i++)
{
ticketTableInsertCMD.Parameters.AddWithValue("#TicketID", ticketID);
ticketTableInsertCMD.Parameters.AddWithValue("#num1", dgv_ticket.Rows[i].Cells[0].Value);
ticketTableInsertCMD.Parameters.AddWithValue("#num2", dgv_ticket.Rows[i].Cells[1].Value);
ticketTableInsertCMD.Parameters.AddWithValue("#num3", dgv_ticket.Rows[i].Cells[2].Value);
ticketTableInsertCMD.Parameters.AddWithValue("#num4", dgv_ticket.Rows[i].Cells[3].Value);
ticketTableInsertCMD.Parameters.AddWithValue("#num5", dgv_ticket.Rows[i].Cells[4].Value);
sqlConn.Open();
ticketTableInsertCMD.ExecuteNonQuery();
sqlConn.Close();
}
Just clear sqlCommand parameters in start of for loop and set ticketID for each row:
for (int i = 0; i < dgv_ticket.Rows.Count; i++)
{
ticketID = (int)selectTicketIDCMD.ExecuteScalar()+1;
ticketTableInsertCMD.Parameters.Clear();
ticketTableInsertCMD.Parameters.AddWithValue("#TicketID", ...
Found a way to do it properly I added the parameters outside the loop and then just changed their values inside the loop.
ticketTableInsertCMD.Parameters.Add("#TicketID", SqlDbType.Int);
ticketTableInsertCMD.Parameters.Add("#num1", SqlDbType.Int);
ticketTableInsertCMD.Parameters.Add("#num2", SqlDbType.Int);
ticketTableInsertCMD.Parameters.Add("#num3", SqlDbType.Int);
ticketTableInsertCMD.Parameters.Add("#num4", SqlDbType.Int);
ticketTableInsertCMD.Parameters.Add("#num5", SqlDbType.Int);
for (int i = 0; i < dgv_ticket.Rows.Count-1; i++)
{
ticketTableInsertCMD.Parameters["#TicketID"].Value = ticketID;
ticketTableInsertCMD.Parameters["#num1"].Value = int.Parse(dgv_ticket.Rows[i].Cells[0].Value.ToString());
ticketTableInsertCMD.Parameters["#num2"].Value = int.Parse(dgv_ticket.Rows[i].Cells[1].Value.ToString());
ticketTableInsertCMD.Parameters["#num3"].Value = int.Parse(dgv_ticket.Rows[i].Cells[2].Value.ToString());
ticketTableInsertCMD.Parameters["#num4"].Value = int.Parse(dgv_ticket.Rows[i].Cells[3].Value.ToString());
ticketTableInsertCMD.Parameters["#num5"].Value = int.Parse(dgv_ticket.Rows[i].Cells[4].Value.ToString());
I'm having two mysql TABLES - ORDER and ORDER_DETAILS the relationship is one order has many order_details.
After creating an order, I get the last inserted id and I need to insert order details with last inserted last order id via a for loop. But here it is showing a message
#order_id has already been defined
when having more than one order details. If it is one order detail it works.
How can I defined this order id here? Is this a wrong way? Here is my code:
private void button2_Click(object sender, EventArgs e)
{
try
{
DateTime localDate = DateTime.Now;
MydbConnection db = new MydbConnection();
MySqlConnection con = db.connection();
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "insert into orders (created) values(#localDate)";
cmd.Parameters.AddWithValue("#localDate", localDate);
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
long lastId = cmd.LastInsertedId;//Last inserted id
for (int i = 0; i < listView3.Items.Count; i++)
{
cmd.CommandText = "insert into order_details (order_id,product_id,qty) values(#order_id,#product_id,#qty)";
cmd.Parameters.AddWithValue("#order_id", lastId);
cmd.Parameters.AddWithValue("#product_id", 1);
cmd.Parameters.AddWithValue("#qty", listView3.Items[i].SubItems[1]);
cmd.ExecuteNonQuery();
}
}
catch (Exception es) {
MessageBox.Show("Order not saved! "+es.Message);
}
}
Each time you call cmd.Parameters.AddWithValue you adding one more parameters. When you call it twice or more with parameter with same name duplicated parameters appears.
When your listView3.Items.Count contains at least two elements, your code will add duplicated #order_id, #product_id and #qty.
You need either to recreate your MySqlCommand cmd on each iteration, or change value of parameters when they already added.
First approach is more error-prone. Al least because you will not use other data left from previous usage (#localDate parameter you adding before loop, that otherwise will be also passed to database).
for (int i = 0; i < listView3.Items.Count; i++)
{
cmd = new MySqlCommand();
cmd.Connection = con;
cmd.CommandText = "insert into order_details (order_id,product_id,qty) values(#order_id,#product_id,#qty)";
cmd.Parameters.AddWithValue("#order_id", lastId);
cmd.Parameters.AddWithValue("#product_id", 1);
cmd.Parameters.AddWithValue("#qty", listView3.Items[i].SubItems[1]);
cmd.ExecuteNonQuery();
}
You can't keep adding the same command parameters over and over. Instead, why not re-use them:
private void button2_Click(object sender, EventArgs e)
{
try
{
DateTime localDate = DateTime.Now;
MydbConnection db = new MydbConnection();
MySqlConnection con = db.connection();
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "insert into orders (created) values(#localDate)";
cmd.Parameters.AddWithValue("#localDate", localDate);
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
long lastId = cmd.LastInsertedId;//Last inserted id
cmd.CommandText = "insert into order_details (order_id,product_id,qty) values(#order_id,#product_id,#qty)";
MySqlCommandParameter orderIdParam, productIdParam, qtyParam;
for (int i = 0; i < listView3.Items.Count; i++)
{
if (i == 0)
{
orderIdParam = cmd.Parameters.AddWithValue("#order_id", lastId);
productIdParam = cmd.Parameters.AddWithValue("#product_id", 1);
qtyParam = cmd.Parameters.AddWithValue("#qty", listView3.Items[i].SubItems[1]);
}
else
{
orderIdParam.Value = lastId;
productIdParam.Value = 1;
qtyParam.Value = listView3.Items[i].SubItems[1];
}
cmd.ExecuteNonQuery();
}
}
catch (Exception es) {
MessageBox.Show("Order not saved! "+es.Message);
}
}
Note: I haven't actually tested this code and probably have the wrong types and member names for MySqlCommandParameter and MySqlCommandParameter.Value but you should still get the idea.
It would probably be better coding practice to Add the parameters first (before entering the loop) and not have the if block in the loop instead of using AddWithValue for the first loop iteration. You could also think about Clear-ing the parameter collection on the command and calling AddWithValue every iteration. Ultimately, I think it is best to re-use the parameters, though - certainly preferable to creating a new instance of MySqlCommand every iteration - there might be thousands of iterations!
Firstly, as hinted in the comments, you need a way to remove the parameters added in the previous iteration of the loop:
cmd.Parameters.Clear();
placed after cmd.ExecuteNonQuery() will do that for you. But beware! The SQL connection, and the SQL command, .NET base types implement IDisposable, and you should despose of them properly when you're done with them to avoid things like connection pool blocking. I've recently had to rescue an old project from poorly managed disposables and a) it was a pain but b) the result was a much faster, more reliable app. You might want to look in to that when you've solved your current problem.
Tried finding similar from my problem but it seems there are too many unreachable code detectedmy table consist of 4 rows and I tried this code
using (MySqlConnection conn = new MySqlConnection(myConnection))
{
conn.Open();
MySqlCommand cmd = new MySqlCommand(query, conn);
int num = Convert.ToInt32(cmd.ExecuteScalar());
MySqlCommand cmd1 = new MySqlCommand(query2, conn);
MySqlDataReader reader = cmd1.ExecuteReader();
while (reader.Read())
{
for (int a = 0; a <= num; a++)
{
List li = new List();
li.linkLabel1.Text = reader["TitleAnime"].ToString();
flowLayoutPanel1.Controls.Add(li);
// break;
}
}
conn.Close();
}
but it gives me 16 values and thats too much then I tried putting break inside the for loop and I was able to achieve my goal and it gives me 4 values which is the same on my table but there seems to be an error called unreachable code detected..should I ignore it since I was able to get what I need? or is there another way for that
I'm pretty sure you are doing one query too much there, and you are getting NxN results because of that first query and of that for loop.
Try something like this:
using (MySqlConnection conn = new MySqlConnection(myConnection))
{
conn.Open();
MySqlCommand cmd1 = new MySqlCommand(query2, conn);
MySqlDataReader reader = cmd1.ExecuteReader();
while (reader.Read())
{
List li = new List();
li.linkLabel1.Text = reader["TitleAnime"].ToString();
flowLayoutPanel1.Controls.Add(li);
}
conn.Close();
}
If that does the job, consider in changing the name of the query1 and cmd1 to query and cmd since now you'll have only one of each
It's quite simple. You're running a for loop based on the number of entries you have in your database from the command object.
The read method will iterate four times if that's how many records you have in your database. Refer to this article for more information: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.read(v=vs.110).aspx.
In short, just remove the for loop and you'll get the result you want.
Try something like this:
while (reader.Read())
{
flowLayoutPanel1.Controls
.Add(new List { linkLabel1.Text = reader["TitleAnime"].ToString()});
}