I seem to be having problems with my method that will return an Integer. I am attempting to modify the rows of a particular column with this returning Integer. The database will update the pre-existing column values with this new returned value. However, it appears that every row is being modified to the LAST row's value, regardless of what the specific row held previously. I am sure my code is just overwriting the variable, but I am wondering where. Here is my method; would appreciate feedback.
private int extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
ParsingHelper helper = null // different class - no issues with this.
String query = "SELECT device FROM dLogger";
OdbcCommand command = new OdbcCommand(query, this.connection);
List<Int32> list = new List<Int32>();
OdbcDataReader reader = null;
reader = command.ExecuteReader();
while (reader.Read())
{
list.Add(reader.GetInt32(0));
for (int i = 0; i < reader.FieldCount; i++)
{
helper = new ParsingHelper();
helper.assemble(list[i]);
}
}
return helper.getFirst();
}
No problem with the ParsingHelper here, it does the correct work. My problem is the overwriting. I thought a List would alleviate this issue but I am missing something, evidently.
EDIT: Would this approach work better?
while(reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
list.Add(reader.GetInt32(i));
//....
}
If my table originally looked like this:
ColA
1
2
3
4
And my function, for example, multiplied each number by 2. The new column would look like
ColA
8 // rather than 2
8 // rather than 4
8 // rather than 6
8 // 8 is the last value - therefore, correct.
So you see, I am running into some overwriting issues here. It appears the reader will read effectively and to the last row but it is not modifying values correctly, it is only assigning each value to the last value.
EDIT:
Here is where I am updating my database:
private void update()
{
String query = "UPDATE dLogger SET device = ?";
OdbcCommand command = new OdbcCommand(query, this.connection);
if (this.connection.State != ConnectionState.Open)
{
this.connection.Open();
}
command.Parameters.AddWithValue("?", extractValue());
}
Also, here is my simple Parsing Helper Class assemble()
private void assemble(int value)
{
setFirst(value);
}
private void setFirst(int value)
{
value = value * 2;
}
Just change your
String query = "SELECT device FROM dLogger";
to
String query = "UPDATE dLogger SET device=device*2";
thus:
private void extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
String query = "UPDATE dLogger SET device=device*2";
OdbcCommand command = new OdbcCommand(query, this.connection);
command.Execute();
}
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
After browsing a multitude topics on the phenomenon of SqlDataReader.HasRows which always returns true even with empty result (and especially when it is about an SQL query with an aggregate), I dry completely on my code
However my example is very simple and HasRows returns True, FieldCount returns 1 even when there is no phpMyAdmin side line.
query = "SELECT FK_BarId FROM tlink_bar_beer WHERE FK_BeerId = " + sqlDataReader.GetInt32(0);
MySqlConnection sqlConnexionList = new MySqlConnection("server=localhost;database=beerchecking;uid=root;password=;");
MySqlCommand commandList = new MySqlCommand(query, sqlConnexionList);
sqlConnexionList.Open();
int[] BarsIds;
using (MySqlDataReader sqlDataReaderList = commandList.ExecuteReader())
{
if (sqlDataReaderList.HasRows)
{
try
{
BarsIds = new int[sqlDataReaderList.FieldCount];
int counter = 0;
if (sqlDataReaderList.Read())
{
while (sqlDataReaderList.Read())
{
int id = sqlDataReaderList.GetInt32(counter);
BarsIds[counter] = id;
counter++;
}
}
}
finally
{
sqlDataReaderList.Close();
}
}
else
{
BarsIds = new int[0];
}
}
sqlConnexionList.Close();
Do you know how to get HasRows false when there is no rows like in phpMyAdmin result?
Thanks for reading.
I prefer to use an auxiliar DataTabe instead DataReader, something like this:
DataTable dt = new DataTable();
dt.Load(commandList.ExecuteReader());
if(dt.Rows.Count > 0){
//rows exists
}else{
//no rows
}
I am fetching a column from database of char(2) data type.
On an Event, I am changing the char data type to int and incrementing it by 1, with this code:
int i = 0;
using (SqlConnection sqlCon = new SqlConnection(Login.connectionString))
{
string commandString = "SELECT MAX(CAST(Category_Code as INT)) FROM Category;";
SqlCommand sqlCmd = new SqlCommand(commandString, sqlCon);
sqlCon.Open();
SqlDataReader dr = sqlCmd.ExecuteReader();
while (dr.Read())
{
i = 1;
if (dr[0] == null)
{
Ctgry_CtgryCodeCb.Text = "1";
}
else
{
int cat_next_code = int.Parse(dr[0].ToString());
Ctgry_CtgryCodeCb.Text = (cat_next_code + 1).ToString();
}
}
}
It is working properly but not for the first time (doesn't add 1 to empty or 0) as column is empty.It shows some error that it is not is correct format to convert. I also set the default value of the column to 0, but it shows ((0)) as default value.
Any help would be appreciated.
If you are using this code to increment primary key value of the database table, you shouldn't be doing this way. You should be using IDENTITY column feature available in the database.
Since you have not explained why you are not using IDENTITY column, looks like this code is for some other purpose.
As per your code you are getting Maximum value of some column from the database and incrementing it by one in the code.
When the table in the database is empty you not get anything is the reader. So While loop will not be executed at all. So even if you are checking for NullOrEmpty inside the while loop, it will never get executed.
Also you don't need to use SqlDataReader here. Since you are returning only one single value from the query you can use ExecuteScalar method of SqlCommand and get that value. It will be simpler.
var codeFromDb = sqlCmd.ExecuteScalar();
var cat_next_code = 0;
if(!(codeFromDb is DBNull))
{
cat_next_code = Convert.ToInt32(codeFromDb);
}
Ctgry_CtgryCodeCb.Text = (cat_next_code + 1).ToString();
My strong recommendation is to use IDENTITY column instead of doing all this code.
This will help you resolve your current issue.
SqlDataReader is overkill in this case and I don't like to answer for the wrong approach but since you insist consider following.
SqlDataReader dr = sqlCmd.ExecuteReader();
int cat_next_code = 0;
if(dr.Read()) // while is not needed here. There will be only one row in the reader as per the query.
{
i = 1;
if(!dr.IsDBNull(0))
{
cat_next_code = int.Parse(dr[0].ToString());
}
}
Ctgry_CtgryCodeCb.Text = (cat_next_code + 1).ToString();
EDIT: I am not able to format my code below, if any one can fix it.
I am new to sql queries and still learning.
Table Name: CommissionSetupTable.
I want to display #Paisa if gross_amount is between the range of #FromRate and #ToRate
Below is my code:
string paisa;
private void load_commission_setup()
{
SqlCeConnection conn = null;
SqlCeCommand cmd = null;
SqlCeDataReader rdr = null;
try
{
conn =
new SqlCeConnection(
#"Data Source=|DataDirectory|\Database.sdf;Persist Security Info=False");
conn.Open();
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
gross_amount = double.Parse(purchaseBillTableDataGridView[10, rowindex].Value.ToString());
// Gross Amount is between the ranges of FromRate and ToRate.
cmd = new SqlCeCommand("SELECT Paisa FROM CommissionSetupTable WHERE='" + gross_amount.ToString() + "' BETWEEN #FromRate AND #ToRate;", conn);
rdr = cmd.ExecuteReader();
if (rdr == null)
{
}
else
{
while (rdr.Read())
{
paisa = rdr["Paisa"].ToString();
}
rdr.Close();
cmd.Dispose();
}
}
finally
{
conn.Close();
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
purchaseBillTableDataGridView[11, rowindex].Value = paisa;
}
}
The correct syntax to use here is the following
cmd = new SqlCeCommand(#"SELECT Paisa FROM CommissionSetupTable
WHERE #gross BETWEEN FromRate AND ToRate;", conn);
Notice that the two field names should not be prefixed with #, otherwise they will be considered parameters placeholders.
And now, before executing the command, add the parameter for the #gross placeholder
cmd.Parameters.Add("#gross", SqlDbType.Decimal).Value = gross_amount;
I don't know what is the exact datatype of the columns FromRate and EndRate, but
note that you should use the correct datatype for your parameter. Do not pass a string and expect the database engine do the conversion for you. (or worse concatenate your value to the rest of the sql using ToString()). This is always wrong also if sometime the database engine could understand your values.
EDIT
Also, following your comments below, it appears that this line is wrong
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
If your DataGridView has the property AllowUserToAddRow set to True then you want to use
int rowindex = purchaseBillTableDataGridView.Rows.Count - 2;
because the first line points to the empty row added to the DataGridView for inserting a new record.
Only one time, thank you for help !
Public string panda(string lola = #"Server=.\SQLEXPRESS; DataBase=panda; Integrated Security=true;")
{
SqlConnection panda = new SqlConnection(lola);
panda.Open();
return lola;
}
public string Show_details(string Command = "Select name From panda")
{
SqlConnection cn = new SqlConnection(panda());
SqlCommand Show;
SqlDataReader read;
Show = new SqlCommand(Command, cn);
cn.Open();
read = Show.ExecuteReader();
while (read.Read())
{
listBox1.Items.Add(read["name"]);
}
return Command;
}
private void button3_Click(object sender, EventArgs e)
{
Show_details();
}
I'm looking for how to make the reader read data and post it in the listbox only one time !
If I understood your question correctly, you only want to go inside the reader loop once. 'While', no pun intended, there are more efficient ways to go about this, you could declare a bool flag to see if you've hit the loop yet. Once inside the loop, change it to false so when the next time while condition is evaluated, it will evaluate to false ending the loop. See below.
public string Show_details(string Command = "Select name From panda")
{
SqlConnection cn = new SqlConnection(panda());
SqlCommand Show;
SqlDataReader read;
Show = new SqlCommand(Command, cn);
cn.Open();
read = Show.ExecuteReader();
// Declare flag to see if you've hit the reader yet.
bool hasntYetRead = true;
// Add a second condition to determine if to cursor through again
while (read.Read() && hasntYetRead )
{
listBox1.Items.Add(read["name"]);
// Change the flag to false
hasntYetRead = false;
}
return Command;
}
You could use a counter if you want to be able to change the number or iterations in the future.
public string Show_details(string Command = "Select name From panda")
{
SqlConnection cn = new SqlConnection(panda());
SqlCommand Show;
SqlDataReader read;
Show = new SqlCommand(Command, cn);
cn.Open();
read = Show.ExecuteReader();
// declare counter
int counter = 0;
// Add a second condition to determine if to cursor through again
while (read.Read() && counter < 1) //could get counter to count to user input number
{
listBox1.Items.Add(read["name"]);
// Change the flag to false
counter++;
}
return Command;
}