I have a stored procedure that would count the number of times a value is in a database. However in the code behind for loop when it reaches
(int)command.ExecuteScalar(); the loop stops and asks to supply parameter for #invoice2.
Stored Procedure:
select count(*) as CountInvoice where invoice1=#invoice1 or #invoice2=#invoice`2
Code Behind:
using (SqlCommand command = new SqlCommand("sp_Count", conn))
{
foreach (TextBox textBox in placehldr1.Controls.OfType<TextBox>())
{
count += 1;
command.CommandType = CommandType.StoredProcedure;
string invoice = textBox.Text.TrimEnd();
string parameter = string.Format("#invoice{0}", count);
command.Parameters.AddWithValue(parameter, invoice);
int invoiceCount = (int)command.ExecuteScalar();
if (invoiceCount > 0)
{
lblError.Text = "Invoice number already exist";
return;
}
command.Parameters.Clear();
}
Would something like this work better as it factors in the two parameters.
select count(*) as CountInvoice where invoice1=#invoice1 or invoice2=#invoice2
using (SqlCommand command = new SqlCommand("sp_Count", conn))
{
foreach (TextBox textBox in placehldr1.Controls.OfType<TextBox>())
{
count1 += 1;
count2 += 1;
command.CommandType = CommandType.StoredProcedure;
string invoice = textBox.Text.TrimEnd();
string parameter1 = string.Format("#invoice1", count1);
string parameter2 = string.Format("#invoice2", count2);
command.Parameters.AddWithValue(parameter, invoice);
int invoiceCount = (int)command.ExecuteScalar();
if (invoiceCount > 0)
{
lblError.Text = "Invoice number already exist";
return;
}
command.Parameters.Clear();
}
Related
Can anyone help improve performance? Updating the table takes a lot of time.
I am updating the serial number from datagridview to a table called dbo.json
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (string.IsNullOrEmpty(row.Cells[5].Value as string))
{
}
else
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
var cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
using (var con = new SqlConnection(cnn))
{
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
MessageBox.Show("dbo.json is ready");
}
You shouldn't create the connection and command inside such a tight loop - create and open the connection and command ONCE before the loop, and in the loop, only set the parameter values and execute the query for each entry.
Something like this:
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
// define connection string, query text *ONCE* before the loop
string cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
string updateQuery = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2;";
// create connection and command *ONCE*
using (SqlConnection con = new SqlConnection(cnn))
using (SqlCommand cmd = new SqlCommand(updateQuery, cnn))
{
// Define parameters - adapt as needed (don't know the actual datatype they have)
cmd.Parameters.Add("#VAL1", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#VAL2", SqlDbType.VarChar, 100);
// open connection ONCE, for all updates
con.Open();
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
{
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
// set the values
cmd.Parameters["#VAL1"].Value = VAL1;
cmd.Parameters["#VAL2"].Value = VAL2;
// execute query
cmd.ExecuteNonQuery();
}
}
}
// close connection after all updates are done
con.Close();
}
MessageBox.Show("dbo.json is ready");
}
Create the connection ONCE...you're creating a new database connection each time through the loop! And in fact you do not need to create new command objects each time. You can reuse the command object because the parameters are the same. Just clear the params each time through the loop.
Also don't do the grid view count in the loop, set a variable for it.
string query = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
int counter = DgvWhistlSorted.Rows.Count - 2;
using (SqlConnection con = new SqlConnection(cnn))
{
con.Open();
using(SqlCommand cmd = new SqlCommand(cnn,query))
{
cmd.Parameters.Clear();
//Do your loop in here
for (int i = 0; i <= counter; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.ExecuteNonQuery();
}
}
}
A better idea is to do this in one command, by passing all the data in a Table-Value Parameter (TVP):
First create the table type. I don't know your data types, so I'm guessing here. Make sure to match the types to the existing table.
CREATE TYPE dbo.OrderJson (
Order int PRIMARY KEY,
RowN nvarchar(max) NOT NULL
);
Then you can pass the whole thing in one batch. You need to create a DataTable to pass as the parameter, or you can use an existing datatable.
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
var table = new DataTable {
Columns = {
{ "Order", typeof(int) },
{ "RowN", typeof(string) },
},
};
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
table.Rows.Add(DgvWhistlSorted.Rows[i].Cells[0].Value, DgvWhistlSorted.Rows[i].Cells[6].Value)
const string query = #"
UPDATE dbo.json
SET RowN = t.RowN
FROM dbo.json j
JOIN #tmp t ON t.order = j.[A-order];
";
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["sql"].ConnectionString))
using (var cmd = new SqlCommand(query, con))
{
cmd.Parameters.Add(new SqlParameter("#tmp", SqlDbType.Structured) { Value = table, TypeName = "dbo.OrderJson" });
con.Open();
cmd.ExecuteNonQuery();
}
MessageBox.Show("dbo.json is ready");
}
I found that the fastest way would be to save the DATAGRIDVIEW to an SQL table and continue the process with - stored procedure + update query - between two tables - now it flies ...
Thank you all
i want to delete one or more selected items in checkedlistbox. But i dont know how i must to do that. Pls help
here my codes;
private void button2_Click(object sender, EventArgs e)
{
checkedListBox1.Items[checkedListBox1.SelectedIndex].ToString();
SqlCommand del = new SqlCommand("DELETE FROM TABLE1 WHERE personID='" +
personID[checkedListBox1.SelectedIndex] + "' ", con);
con.Open();
for (int i = 0; i < checkedListBox1.Items.Count - 1; i++)
{
if(checkedListBox1.CheckedItems[i]) //this line does not work
{
del.ExecuteNonQuery();
}
}
con.Close();
}
You should use a parameter for security reason.
Next you can set it in the loop with the desired id.
con.Open();
var command = new SqlCommand("DELETE FROM TABLE1 WHERE personID=? ", con);
var param = del.Parameters.Add("#ID", SqlDbType.Int /* or any relevant type */);
foreach ( var item in checkedListBox1.SelectedItems )
{
param.Value = ((TheTypeOfTheItems)item).NameOfTheID;
command.ExecuteNonQuery();
}
con.Close();
Perhaps you can construct only one query by adding some OR to the WHERE clause and execute it one time after the loop.
if ( checkedListBox1.SelectedItems.Count > 0 )
{
con.Open();
var command = new SqlCommand("DELETE FROM TABLE1 WHERE ", con);
var id = ( (TheTypeOfTheItems)checkedListBox1.SelectedItems[index] ).NameOfTheID;
command.CommandText += "personID =? ";
command.Parameters.Add("#ID" + index, SqlDbType.Int).Value = id;
if ( checkedListBox1.SelectedItems.Count > 1 )
for ( int index = 1; index < checkedListBox1.SelectedItems.Count; index++ )
{
command.CommandText += "OR ";
var id = ( (TheTypeOfTheItems)checkedListBox1.SelectedItems[index] ).NameOfTheID;
command.CommandText += "personID =? ";
command.Parameters.Add("#ID" + index, SqlDbType.Int).Value = id;
}
command.ExecuteNonQuery();
con.Close();
}
Note: I can't test the code and I used params with odbc and not sql server.
Help, I am new to asp.net c#. I tried the codes once before but it didn't seems to have this error. So I can't seems to figure where do I make changes to.
Sever error :
There is already an open DataReader associated with this Command which must be closed first.
Codes:
string strConnectionString = ConfigurationManager.ConnectionStrings["FYPDB"].ConnectionString;
SqlConnection myConnect = new SqlConnection(strConnectionString);
string strCommandText = "select promoId FROM FYPDB.dbo.Promotions where membershipType LIKE '%' + #membership + '%' AND defaults LIKE '%' + #defaults + '%'";
try
{
string ddlmembership = ((DropDownList)dvInsertPromotion.FindControl("ddlAddMembershiplist")).SelectedItem.ToString();
string ddlDefault = ((RadioButtonList)dvInsertPromotion.FindControl("RadioButtonList2")).Text.ToString();
DataSet da = dal.retrieveMembership(ddlmembership, ddlDefault);
SiteGridView.DataSource = da;
SiteGridView.DataBind();
SqlCommand cmd = new SqlCommand(strCommandText, myConnect);
cmd.Parameters.Add("#membership", SqlDbType.NVarChar);
cmd.Parameters.Add("#defaults", SqlDbType.NVarChar);
cmd.Parameters["#membership"].Value = ddlmembership;
cmd.Parameters["#defaults"].Value = ddlDefault;
myConnect.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
//get number of count
//int count = da.Tables[0].Rows.Count;
int count = (int)cmd.ExecuteScalar();
if (count == 1)
{
defaultComfirm.Show();
promotion = false;
}
}
else
{
Label6.Text = "error didnt go through";
Label6.ForeColor = System.Drawing.Color.Gray;
Label6.Visible = true;
}
reader.Close();
}
finally
{
myConnect.Close();
}
you are using the same command object two times:
SqlDataReader reader = cmd.ExecuteReader();
int count = (int)cmd.ExecuteScalar();
This is not possible.
It is not necessary to use a reader if you just want to have the count of rows. In this case you can use the ExecuteScalar.
Like this:
string strCommandText = "select COUNT(promoId) FROM FYPDB.dbo.Promotions where membershipType LIKE '%' + #membership + '%' AND defaults LIKE '%' + #defaults + '%'";
try
{
...
int count = (int)cmd.ExecuteScalar();
...
Regards
Oliver
Looking at your code, you just need count of rows matching the criteria.
So if you just need rows count you could use any of these options.
Option 1:
Find out count by reading all rows
int count =0;
while(reader.Read()) count++;
if (count == 1)
{
defaultComfirm.Show();
promotion = false;
}
else
{
...
}
reader.Close();
Option 2:
My preferred choice for this case, Modify your query to return the count (SELECT COUNT(*)).
string strCommandText = "select count(promoId) FROM FYPDB.dbo.Promotions where membershipType LIKE '%' + #membership + '%' AND defaults LIKE '%' + #defaults + '%'";
now,
int count = (int)cmd.ExecuteScalar();
if (count == 1)
{
defaultComfirm.Show();
promotion = false;
}
else
{
...
}
reader.Close();
Option 3:
Other option to convert it to DataTable and get the row count.
using (DataTable dt = new DataTable())
{
dt.Load(reader);
if (dt.Rows.Count == 1)
{
defaultComfirm.Show();
promotion = false;
}
else
{
...
}
reader.Close();
}
try to execute cmd.ExecuteScalar() before cmd.ExecuteReader().
int count = (int)cmd.ExecuteScalar();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
//get number of count
//int count = da.Tables[0].Rows.Count;
if (count == 1)
{
defaultComfirm.Show();
promotion = false;
}
}
I have code that inserts data into a table when a user enters certain values into three boxes on the page.
The boxes are order number, total weight and tracking reference.
I now need to add further functionality to this code and check first to see if the order number exists, if it does i need to update the columns, if it doesn't I need to insert a new row and add data to that.
I was thinking simply, something like IF results = 0, Insert NEW, ELSE update
How can I modify my code to do this?
protected void Page_Load(object sender, EventArgs e)
{
errorLabel.Visible = false;
successLabel.Visible = false;
errorPanel.Visible = false;
}
protected void submitBtn_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
int _orderID = Convert.ToInt32(orderID.Text);
string _trackingID = trackingNumber.Text;
DateTime _date = DateTime.UtcNow;
int _weightID = Convert.ToInt32(weightID.Text);
SqlConnection myConnection = new SqlConnection("Data Source=localhost\\Sqlexpress;Initial Catalog=databasename;User ID=username;Password=password");
SqlCommand myCommand = new SqlCommand("INSERT into Shipment (TrackingNumber, OrderId, ShippedDateUtc, CreatedOnUtc, TotalWeight) VALUES (#tracking, #order, #date, #date, #weight)", myConnection);
try
{
myConnection.Open();
myCommand.Parameters.AddWithValue("#order", _orderID);
myCommand.Parameters.AddWithValue("#tracking", _trackingID);
myCommand.Parameters.AddWithValue("#date", _date);
myCommand.Parameters.AddWithValue("#weight", _weightID);
int rowsUpdated = myCommand.ExecuteNonQuery();
myConnection.Close();
if (rowsUpdated > 0)
{
alertdiv.Attributes.Add("class", "alert alert-success form-signin");
successLabel.Text = "Thank you, tracking details have been updated";
successLabel.Visible = true;
errorPanel.Visible = true;
}
else
{
alertdiv.Attributes.Add("class", "alert alert-error form-signin");
errorLabel.Text = "Oh dear, the order number is not recognised, please check and try again";
errorLabel.Visible = true;
errorPanel.Visible = true;
}
orderID.Text = "";
trackingNumber.Text = "";
weightID.Text = "";
}
catch (Exception f)
{
errorLabel.Text = "This order number does not exist, please check";
errorLabel.Visible = true;
errorPanel.Visible = true;
return;
}
}
}
protected void Signout_Click(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("Login.aspx");
}
You can add some SELECT query before your INSERT statement. So if the SELECT query returns more than one row, it means that you already have that record in the DB, and need to update. So, in general it will be like
SqlCommand cmdCount = new SqlCommand("SELECT count(*) from Shipment WHERE OrderId = #order", myConnection);
cmdCount.Parameters.AddWithValue("#order", _orderID);
int count = (int)cmdCount.ExecuteScalar();
if (count > 0)
{
// UPDATE STATEMENT
SqlCommand updCommand = new SqlCommand("UPDATE Shipment SET TrackingNumber = #tracking, ShippedDateUtc = #date, TotalWeight = #weight", myConnection);
updCommand.Parameters.AddWithValue("#order", _orderID);
updCommand.Parameters.AddWithValue("#tracking", _trackingID);
updCommand.Parameters.AddWithValue("#date", _date);
updCommand.Parameters.AddWithValue("#weight", _weightID);
int rowsUpdated = myCommand.ExecuteNonQuery();
}
else
{
// INSERT STATEMENT
SqlCommand insCommand = new SqlCommand("INSERT into Shipment (TrackingNumber, OrderId, ShippedDateUtc, CreatedOnUtc, TotalWeight) VALUES (#tracking, #order, #date, #date, #weight)", myConnection);
insCommand.Parameters.AddWithValue("#order", _orderID);
insCommand.Parameters.AddWithValue("#tracking", _trackingID);
insCommand.Parameters.AddWithValue("#date", _date);
insCommand.Parameters.AddWithValue("#weight", _weightID);
int rowsUpdated = myCommand.ExecuteNonQuery();
}
Edit:
Or much shorter:
SqlCommand command;
if (count > 0)
{
command = new SqlCommand("UPDATE Shipment SET TrackingNumber = #tracking, ShippedDateUtc = #date, TotalWeight = #weight WHERE OrderId = #order", myConnection);
}
else
{
command = new SqlCommand("INSERT into Shipment (TrackingNumber, OrderId, ShippedDateUtc, CreatedOnUtc, TotalWeight) VALUES (#tracking, #order, #date, #date, #weight)", myConnection);
}
command.Parameters.AddWithValue("#order", _orderID);
command.Parameters.AddWithValue("#tracking", _trackingID);
command.Parameters.AddWithValue("#date", _date);
command.Parameters.AddWithValue("#weight", _weightID);
int rowsUpdated = command.ExecuteNonQuery();
The most efficient way would be to put the functionality into a Stored Procedure, for instance (pseudo-code):
IF EXISTS(SELECT * FROM Orders WHERE OrderNo = #orderNo)
UPDATE ...
ELSE
INSERT ...
If you cannot create a new stored procedure, you can also create a command that contains this Statement though readability is typically worse.
Both approaches require only one DB-request.
If you like receive all data and check if exist any record use HasRows.
using (SqlConnection connection = new SqlConnection("server name"))
{
SqlCommand cmd = new SqlCommand("select * From Shipment where OrderId =#OrderId", connection);
connection.Open();
SqlDataReader sdr = cmd.ExecuteReader();
if (sdr.HasRows==true)//check have any recorder
{
while (sdr.Read())
{
Debug.Print("Exist recorder, example.: "+sdr["_trackingID"]);
}
}
else
{
Debug.Print("not exist recorder");
}
connection.Close();
}
I want to implement a simple delete button for my database. The event method looks something like this:
private void btnDeleteUser_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure?", "delete users",MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
command = new SqlCommand();
try
{
User.connection.Open();
command.Connection = User.connection;
command.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
int flag;
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
command.Parameters.AddWithValue("#id", rowUserID);
flag = command.ExecuteNonQuery();
if (flag == 1) { MessageBox.Show("Success!"); }
dgvUsers.Rows.Remove(row);
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
finally
{
if (ConnectionState.Open.Equals(User.connection.State))
User.connection.Close();
}
}
else
{
return;
}
}
but I get this message:
A variable #id has been declared. Variable names must be unique within
a query batch or stored procedure.
Is there any way to reuse this variable?
Parameters.AddWithValue adds a new Parameter to the command. Since you're doing that in a loop with the same name, you're getting the exception "Variable names must be unique".
So you only need one parameter, add it before the loop and change only it's value in the loop.
command.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
command.Parameters.Add("#id", SqlDbType.Int);
int flag;
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
command.Parameters["#id"].Value = rowUserID;
// ...
}
Another way is to use command.Parameters.Clear(); first. Then you can also add the parameter(s) in the loop without creating the same parameter twice.
Rather than:
command.Parameters.AddWithValue("#id", rowUserID);
Use something like:
System.Data.SqlClient.SqlParameter p = new System.Data.SqlClient.SqlParameter();
Outside the foreach, and just set manually inside the loop:
p.ParameterName = "#ID";
p.Value = rowUserID;
I would use this:
public static class DbExtensions
{
public static void AddParameter(SQLiteCommand command, string name, DbType type, object value)
{
var param = new SQLiteParameter(name, type);
param.Value = value;
command.Parameters.Add(param);
}
}
Then, call this:
DbExtensions.AddParameter(command, "#" + fieldOfSearch[i], DbType.String, value[i]);
Error is because you are adding the same parameter again and again in each iteration of the loop.
I would move that code to a seperate method so that i can call it from multiple places as needed.
public bool DeleteUser(int userId)
{
string connString = "your connectionstring";
try
{
using (var conn = new SqlConnection(connString))
{
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "DELETE FROM tbl_Users WHERE userID = #id";
cmd.Parameters.AddWithValue("#id", userId);
conn.Open();
cmd.ExecuteNonQuery();
return true;
}
}
}
catch(Exception ex)
{
//Log the Error here for Debugging
return false;
}
}
Then call it like this
foreach (DataGridViewRow row in dgvUsers.SelectedRows)
{
int selectedIndex = row.Index;
if(dgvUsers[0,selectedIndex]!=null)
{
int rowUserID = int.Parse(dgvUsers[0,selectedIndex].Value.ToString());
var result=DeleteUser(rowUserID)
}
else
{
//Not able to get the ID. Show error message to user
}
}