I’ve got a problem with this SELECT. When I execute it on MS Access, the result is returning right and fully. When I try to execute it via C#, the same query returns nothing. The previous variant of the SELECT was realized by adapter to show the result of select in dataGridView. But when I execute it, I get only an empty table with column names.
private void getByObjectAndPollutant(string Object, string Pollutant,
int firstYear, int lastYear)
{
List<string> result = new List<string>();
// line breaks added without concatenation, for readability.
cmd = new OleDbCommand("SELECT object_name, p_name, mpc, emission_concentration,
[year] FROM Pollutants INNER JOIN (Objects INNER JOIN Emissions
ON Objects.o_id = Emissions.e_o_id) ON Pollutants.p_id = Emissions.e_p_id
WHERE (object_name = '" + Object + "' AND p_name = '" + Pollutant + "' AND
[year] BETWEEN " + firstYear + " AND " + lastYear + ") ;", con);
con.Open();
reader = cmd.ExecuteReader();
while (reader.Read()) {
var myString = reader.GetString(0);
result.Add(myString);
richTextBox1.Text += myString;
}
con.Close();
}
This is how I call it in program:
private void button5_Click(object sender, EventArgs e) {
string localObject = comboBox5.Text.ToString();
string localPollutant = comboBox4.Text.ToString();
int localFirstYear = Convert.ToInt32(comboBox6.Text);
int localLastYear = Convert.ToInt32(comboBox7.Text);
getByObjectAndPollutant(localObject,localPollutant,localFirstYear,localLastYear);
}
This is SELECT query in MS Access:
SELECT object_name, p_name, mpc, emission_concentration, year
FROM Pollutants
INNER JOIN (Objects
INNER JOIN Emissions ON Objects.o_id=Emissions.e_o_id)
ON Pollutants.p_id=Emissions.e_p_id
WHERE object_name="some name" And p_name="some name" And year Between 2011 And 2015;
And this was the previous code of method:
private void getByObjectAndPollutant(string Object, string Pollutant, int firstYear,
int lastYear)
{
con.Open();
DataTable dt = new DataTable();
adapt = new OleDbDataAdapter(this long select);
adapt.Fill(dt);
dataGridView2.DataSource = dt;
con.Close();
}
Main reason I use reader or adapter is to get the result of SELECT query. Is this SELECT returning something? When this works I will then code method to draw chart based on SELECT.
Could be the combos/dropdownlists. Weirdly, comboBox5.Text returns the selected value, not the text.
Try changing to
string localObject = comboBox5.SelectedItem.Text;
string localPollutant = comboBox4.SelectedItem.Text;
Hello! Finally I find the solution. The main aim of SELECT was to get some data from DataBase, but while I getting it I use not ID of an
Item, I use Name of an Item and it return no result. So i think let me
try to make condition with ids.
So the code of working function:
private void getByObjectAndPollutant(int Object, int Pollutant, int firstYear, int lastYear)
{
con.Open();
DataTable dt = new DataTable();
adapt = new OleDbDataAdapter("SELECT mpc, emission_concentration FROM Pollutants INNER JOIN (Objects INNER JOIN Emissions ON Objects.o_id = Emissions.e_o_id) ON Pollutants.p_id = Emissions.e_p_id WHERE Objects.o_id=" + Object + " AND Pollutants.p_id=" + Pollutant + " AND Emissions.[year] BETWEEN " + firstYear + " AND " + lastYear + ";", con);
adapt.Fill(dt);
dataGridView2.DataSource = dt;
con.Close();
}
The code of working function call:
private void button5_Click(object sender, EventArgs e)
{
int localObject = Convert.ToInt32(comboBox4.SelectedValue.ToString());
int localPollutant = Convert.ToInt32(comboBox5.SelectedValue.ToString());
int localFirstYear = Convert.ToInt32(comboBox6.SelectedItem.ToString());
int localLastYear = Convert.ToInt32(comboBox7.SelectedItem.ToString());
getByObjectAndPollutant(localObject,localPollutant,localFirstYear,localLastYear);
}
And in result DataGridView I get the right data I`m happy so much!!! Thanks to ALL of You who was trying to help me!!!
Related
Hi when i trying to select the date I get also the time, but I need to do group by date without the time.
I also tried this: GROUP BY CAST(Orders.DatePlacing AS DATE), and I tried Convert , DATE(Orders.DatePlacing) and I tried this its not working for me.
Microsoft.ACE.OLEDB.12.0
public Product[] ProfitPrice(DateTime minDate, DateTime maxDate)
{
maxDate=maxDate.AddDays(1);
DataSet ds = new DataSet();
ArrayList products = new ArrayList();
string cmdStr = "SELECT Sum(((Products.PriceSell - Products.Price) * OrdersDetails.ProductCount)) AS Expr1,Orders.DatePlacing FROM Products " +
"INNER JOIN(Orders INNER JOIN OrdersDetails ON Orders.OrderId = OrdersDetails.OrderId) " +
"ON Products.ProductID = OrdersDetails.ProductId WHERE [Orders.DatePlacing] BETWEEN #" + minDate.ToString("yyyy-MM-dd") + "#" + "AND" +
" #" + maxDate.ToString("yyyy-MM-dd") + "# "+
"GROUP BY Orders.DatePlacing";
using (OleDbCommand command = new OleDbCommand(cmdStr))
{
ds = GetMultipleQuery(command);
}
DataTable dt = new DataTable();
try
{
dt = ds.Tables[0];
}
catch { }
foreach (DataRow tProduct in dt.Rows)
{
StatisticsFillArray(tProduct, products);
}
return (Product[])products.ToArray(typeof(Product));
}
private void StatisticsFillArray(DataRow tProduct, ArrayList products)
{
Product productsData = new Product();
productsData.PriceTotal = Convert.ToDouble(tProduct[0].ToString());
productsData.EntryDate = tProduct[1].ToString();
products.Add(productsData);
}
Accordingly to my comment to the question...
your code is sql injection vulnerable!
Do NOT use concatenated string, use parameterized queries instead.
Use table/column aliases!
To reject time part from datetime column, use DateSerial function
How? Take a look at below code:
string sql = #"SELECT Sum(((p.PriceSell - p.Price) * od.ProductCount)) AS Expr1, DateSerial(Year(o.DatePlacing), Month(o.DatePlacing), Day(o.DatePlacing)) DatePlacing
FROM Products p
INNER JOIN(Orders o INNER JOIN OrdersDetails od
ON o.OrderId = od.OrderId)
ON p.ProductID = od.ProductId
WHERE DateSerial(Year(o.DatePlacing), Month(o.DatePlacing), Day(o.DatePlacing)) BETWEEN #mindate AND #maxdate
GROUP BY DateSerial(Year(o.DatePlacing), Month(o.DatePlacing), Day(o.DatePlacing))";
DataTable dt = new DataTable();
using (OleDbConnection oConn = new OleDbConnection(yourConnectionStringHere))
{
oConn.Open();
using (OleDbCommand oComm = new OleDbCommand(yourCommandHere, oConn))
{
oComm.Parameters.Add(new OleDbParameter(){"#mindate", OleDbType = OleDbType.Date, Value = minDate},);
oComm.Parameters.Add(new OleDbParameter(){"#maxdate", OleDbType = OleDbType.Date, Value = maxDate},);
using (OleDbDataReader oRdr = oComm.ExecuteReader())
dt.Load(oRdr);
}
}
I am attempting to fill a datatable with the following query:
SELECT
recipes.Name, Instructions, recipes.Preperation_Time, Author
FROM
RecipeIngredients
INNER JOIN
recipes ON recipes.Recipe_ID = RecipeIngredients.Recipe_ID
INNER JOIN
Ingredients ON Ingredients.Ingredient_ID = RecipeIngredients.Ingredient_ID
WHERE
ingredients.Name IN ('eggs')
When I run this query inside an .sql file I get the desired results:
However, when I attempt to fill a datatable with the query, I do not get any results. This is not an issue with my database nor the code as when I use a simple query, such as:
"Select * FROM recipes"
I get all recipes inside my datagridview:
Am I missing something here?
Here is my code for the form and the Database connections class.
private void resultsWindow_Load(object sender, EventArgs e)
{
// Format ingredient array for SQL Syntax
for(int i = 0; i< ingredientCount; i++)
{
string ing = ingredientArray[i];
string editedIng = "'" + ing + "'";
ingredientArray[i] = editedIng;
}
string ingredientString = string.Join(", ", ingredientArray);
// get connection string
string connectionString = Properties.Settings.Default.ConnectionString;
DataTable recipeDataTable = new DataTable();
conn = new DatabaseConnections(connectionString);
conn.openConnection();
// Get datatable
recipeDataTable = conn.getRecipes(ingredientString);
// Display data in grid view
recipesDataGrid.DataSource = recipeDataTable;
recipesDataGrid.Refresh();
}
public DataTable getRecipes(string ingredientString)
{
string sqlString = ("SELECT recipes.Name, Instructions, recipes.Preperation_Time, Author FROM RecipeIngredients" +
" INNER JOIN recipes ON recipes.Recipe_ID = RecipeIngredients.Recipe_ID" +
" INNER JOIN Ingredients ON Ingredients.Ingredient_ID = RecipeIngredients.Ingredient_ID" +
" WHERE ingredients.Name IN ('eggs')");
string sqlString_ = ("Select * FROM recipes");
DataTable recipeDataTable = new DataTable();
openConnection();
SqlCommand cmd = new SqlCommand(sqlString_, connectionToDB);
SqlDataAdapter da = new SqlDataAdapter(cmd);
// Fill dataset
da.Fill(recipeDataTable);
closeConnection();
return recipeDataTable;
}
I didn't try your code, but probably you just added the data in the table and it's not yet validated.
Could you try this statement in your T-SQL editor:
COMMIT TRANSACTION;
I am writing a program in which the user can filter results from a database by 3 textboxes, however, the results are not being filtered correctly, because if one box is left empty, it doesn't display anything
private void textBox1_TextChanged(object sender, EventArgs e)
{
con = new SQLiteConnection(cs);
con.Open();
if ((textBox2.Text==""||textBox.Text3=="")&&textBox1.Text!="")
{
adapt = new SQLiteAdapter("select data1, data2 from DataTable where data1 like '" + textBox1.Text + "%'", con);
dt = new DataTable();
adapt.Fill(dt);
dataGridView1.Source = dt;
}
else if(textBox1.Text !="")
{
adapt = new SQLiteAdapter("select data1, data2 from DataTable where data1 like '" + textBox1.Text + "%' and data2 like '" + textBox2.Text + "%' and substr(data2,-2) like '" + textBox3.Text +"'", con);
dt = new DataTable();
adapt.Fill(dt);
dataGridView1.Source = dt;
}
con.close();
}
That is the code that I am using on one of the textboxes, for the other two it look almost the same, except I change the if clause conditions.
Do I have to write 9 different clauses for each textbox, so that I cover all the options? Is there a right way?
I would parameterize the query to prevent sql injection and use the IFNULL function to help you. This way you have one query to cover all scenarios. If any textbox is empty, the LIKE clause for that item will basically not filter anything out:
string qry = #"SELECT
data1,
data2
FROM DataTable
WHERE
data1 LIKE IFNULL(#data1, data1) AND
data2 LIKE IFNULL(#data2, data2) AND
SUBSTR(data2, -2) LIKE IFNULL(#data3, data3)";
To create the parameters get the textbox values, set the parameter value to null if the textbox is empty. Do this for all 3 textboxes:
string data1 = null;
if(!string.IsNullOrWhiteSpace(textbox1.Text))
{
data1 = textbox1.Text + "%";
}
SqlLiteCommand cmd = new SqlLiteCommand(qry, con);
SqlLiteParameter parData1 = new SqlLiteParameter("#data1", (object)data1 ?? DBNull.Value);
cmd.Parameters.Add(parData1);
Now you can execute that command.
In the Constructor or Form_Load wire up the TextBoxes change events to the one handler:
textBox1.TextChanged += textBox1_TextChanged;
textBox2.TextChanged += textBox1_TextChanged;
textBox3.TextChanged += textBox1_TextChanged;
And build up the WHERE clause dynamically instead of 9 conditions:
con = new SQLiteConnection(cs);
con.Open();
StringBuilder sb = new StringBuilder();
sb.Append("select * from DataTable where ");
foreach (Control c in this.Controls) {
TextBox t = c as TextBox;
if (t != null) {
if (t.Length > 0) {
//In design-time set the TextBox Tag property to the SQL column name
sb.Append(t.Tag.ToString() + " like '" + t.Text + "%' and ");
}
}
}
string SQL = sb.ToString();
if (SQL.Length > 0) {
SQL = SQL.Substring(0, SQL.Length-5);
}
adapt = new SQLiteAdapter(SQL, con);
dt = new DataTable();
adapt.Fill(dt);
dataGridView1.Source = dt;
You should also use a Stored Procedure or pay
Parameterized commands.
I have two checkbox lists, one for a division and one for a course. One division can have many courses so what I want to do is have my user select whichever divisions they want as there are only 10 then display the courses in another checkbox list which are offered by the divisions that have been selected.
Precursor info:
private DataTable GetData(string query)
{
string constr = ConfigurationManager.ConnectionStrings["DatabaseName"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = query;
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
using (DataSet ds = new DataSet())
{
DataTable dt = new DataTable();
sda.Fill(dt);
return dt;
}
}
}
}
}
This is my code where I bind the data to the Divisions checkbox list:
protected void GetDiv()
{
string query = " Select distinct uio.OFFERING_ORGANISATION as [Div], ou.FES_FULL_NAME as [Division] From UNIT_INSTANCE_OCCURRENCES uio " +
" inner join ORGANISATION_UNITS ou on uio.OFFERING_ORGANISATION = ou.ORGANISATION_CODE " +
" inner join REPORTYEARS ry on uio.CALOCC_OCCURRENCE_CODE = ry.CAL_OCC " +
" Where ry.REPORT_YEAR = (select DETAILS from EF_REFERENCE_DATA Where REC_TYPE = 'Rep_Year') and uio.OFFERING_ORGANISATION is not null Order By [Division] ";
cbDivSelect.DataSource = GetData(query);
cbDivSelect.DataTextField = "DIVISION";
cbDivSelect.DataValueField = "DIV";
cbDivSelect.DataBind();
}
I use this to get a list of the divisions selected:
protected void cbDivSelect_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (ListItem item in cbDivSelect.Items)
{
if (item.Selected) Divisions.Add(item);
}
}
and then this to display the Courses:
protected void GetCourse()
{
foreach(ListItem item in Divisions)
{
string GetCourses = "SELECT distinct Course_Code,Course_Code + ' - ' + Marketing_Title AS COURSE, Parent FROM e_prospectus WHERE (Div = '" + item.Value.ToString() + "') ORDER BY Course_Code";
cbCourseSelect.DataSource = GetData(GetCourses);
cbCourseSelect.DataTextField = "COURSE";
cbCourseSelect.DataValueField = "Course_Code";
cbCourseSelect.DataBind();
}
}
Now currently it only displays the lowest course in the list which has been selected, i'm assuming because i keep changing the data source and it doesn't accumulate but is there any way i can change my code to accommodate what i want? thanks
Create one DataTable as DataSource and Merge() the results of GetData() into it.
This would build the whole set in memory. Depending how many Courses you expect using FakeisMe approach of building one query to get all courses out of the database at once might be a lot faster.
string divisions = string.empty ;
foreach(ListItem item in Divisions)
{
divisions = divisions + item.Value.tostring() + ",";
}
if (divisions != string.empty)
{
divisions = divisions.Remove(divisions.Length -1, 1)
string GetCourses = "SELECT distinct Course_Code,Course_Code + ' - ' + Marketing_Title AS COURSE, Parent FROM e_prospectus
WHERE (Div in (" + divisions + ")) ORDER BY Course_Code";
cbCourseSelect.DataSource = GetData(GetCourses);
cbCourseSelect.DataTextField = "COURSE";
cbCourseSelect.DataValueField = "Course_Code";
cbCourseSelect.DataBind();
}
I'm selecting about 20,000 records from the database and then I update them one by one.
I looked for this error and I saw that setting the CommandTimeout will help, but not in my case.
public void Initialize()
{
MySqlConnectionStringBuilder SQLConnect = new MySqlConnectionStringBuilder();
SQLConnect.Server = SQLServer;
SQLConnect.UserID = SQLUser;
SQLConnect.Password = SQLPassword;
SQLConnect.Database = SQLDatabase;
SQLConnect.Port = SQLPort;
SQLConnection = new MySqlConnection(SQLConnect.ToString());
}
public MySqlDataReader SQL_Query(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
MySqlDataReader query_result = sql_command.ExecuteReader();
return query_result;
}
public void SQL_NonQuery(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
sql_command.ExecuteNonQuery();
}
And here is my method which makes the select query:
public void CleanRecords()
{
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
UpdateRecords(cashData["id"].ToString());
}
}
SQLActions.SQL_Close();
}
And here is the method which makes the update:
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id`='" + rowID + "'");
SQLActions.SQL_Close();
}
Changing the database structure is not an option for me.
I thought that setting the timeout to the maxvalue of int will solve my problem, but is looks like this wont work in my case.
Any ideas? :)
EDIT:
The error which I get is "Fatal error encoutered during data read".
UPDATE:
public void CleanRecords()
{
StringBuilder dataForUpdate = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while (cashData.Read())
{
if (cashData["income_money"].ToString() == cashData["total"].ToString())
{
dataForUpdate.Append(delimiter);
dataForUpdate.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(dataForUpdate.ToString());
}
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowID + ")");
SQLActions.SQL_Close();
}
You may be able to use
UPDATE cash_data .... WHERE id IN (SELECT ....)
and do everything in one go. Otherwise, you could do it in two steps: first the select collects all the ids, close the connection and then do the update in obne go with all the ids.
The code for the second option might look something like this:
public void CleanRecords()
{
StringBuilder builder = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
builder.Append(delimiter);
builder.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(builder.ToString());
}
public void UpdateRecords(string rowIDs)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowIDs + ")";
SQLActions.SQL_Close();
}
There are multiple problem:
First: You have reading information around 20K using data reader and then doing update one by one in reader itself. Reader holds the connection open until you are finished. So this is not the good way to do it. Solution: We can read the information using Data Adapter.
Second: Rather than doing one by one update, we can update in bulk in one go. There are multiple option for bulk operation. In SQL u can do either by sending information in XML format or u can use Table Valued Parameter (TVP) (http://www.codeproject.com/Articles/22205/ADO-NET-and-OPENXML-to-Perform-Bulk-Database-Opera) OR (http://dev.mysql.com/doc/refman/5.5/en/load-xml.html)