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.
Related
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!!!
This is my first question on this site so i apologise in advance if I format it incorrectly.
I am creating a system which should be able to search a database (dataGridView) using multiple checkboxes. I found some code online to search it using 3 checkboxes but am unsure how to extend this. I will need to be able to search using 50+ checkboxes. The following code is executed upon pressing of a search button which will display corresponding rows in my database. I want to know to most efficient way to extend this solution to 50+ checkboxes.
private void button1_Click(object sender, EventArgs e)
{
String filterdata = "";
if (checkBox1.Checked)
{
if (checkBox2.Checked || checkBox3.Checked)
{
filterdata = "'T05A1.1',";
}
else
{
filterdata = "'T05A1.1'";
}
}
if (checkBox2.Checked)
{
if (checkBox3.Checked)
{
filterdata = filterdata + "'C16D6.2',";
}
else
{
filterdata = filterdata + "'C16D6.2'";
}
}
if (checkBox3.Checked)
{
filterdata = filterdata + "'F41E7.3'";
}
con.Open();
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
//cmd.CommandText = "Select * from Table1 where elegansgeneID ='" + filterdata + "'";
cmd.CommandText = "Select * from Table1 where elegansgeneID in(" + filterdata + ")";
cmd.ExecuteNonQuery();
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
dataGridView1.DataSource = dt;
con.Close();
}
Try this more shorter approach:
private void Button1_Click(object sender, EventArgs e)
{
var values = new List<string>();
if (checkBox1.Checked)
values.Add("'T05A1.1'");
if (checkBox2.Checked)
values.Add("'C16D6.2'");
if (checkBox3.Checked)
values.Add("'F41E7.3'");
// and so on
String filterdata = string.Join(",", values);
...
}
Alexander Petrov's answer is correct.
But if you are getting more than 50 Check boxes then I would suggest you use CheckBoxList. The code becomes more simpler then.
public Form1()
{
InitializeComponent();
List<string> filters = new List<string> { "T05A1.1", "C16D6.2", "F41E7.3" };
checkedListBox1.Items.Clear();
foreach (string filter in filters)
{
checkedListBox1.Items.Add(filter);
}
}
private void button1_Click(object sender, EventArgs e)
{
List<string> selectedFilter = new List<string>();
for (int i = 0; i < checkedListBox1.CheckedItems.Count; i++)
{
selectedFilter.Add("'" + checkedListBox1.CheckedItems[i].ToString() + "'");
}
string query = "Select * from Table1 where elegansgeneID in(" + string.Join(",", selectedFilter) + ")";
}
With CheckBoxList, you can just add your filters in the filters List variable and it will generate your list. This will keep your code short as well.
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 have following combo boxes bound with data from a sql table.
cmbClassification
cmbAuthor
cmbPublisher
cmbType
I need to extract data based on selected values in these combo boxes.
For example: If I left cmbClassification blank and select values for other 3 combo boxes, my datagridview should show all the data for specific author, specific publisher & specific type regardless of the classification.
I know how to do this for two searching items (using if). but i have 4 items and no idea.
Code for two instances
I think there are 10 search patterns for these 4 combo boxes. how can i handle this? If there are only 2 combo boxes there will be 3 patterns which i will handle as follows:
string con = #"Data Source=ABU_DHABI-1\SQLEXPRESS;Initial Catalog=SLIS;Integrated Security=True";
private void btnView_Click(object sender, EventArgs e)
{
if (cmbAuth.Text == "" && cmbClassi.Text =="")
{
SqlConnection Icon = new SqlConnection(con);
String Query = "SELECT * FROM Books";
SqlCommand Command = new SqlCommand(Query, Icon);
SqlDataAdapter da = new SqlDataAdapter(Command);
DataTable dt = new DataTable();
da.Fill(dt);
BrDataGrid.DataSource = dt;
}
if (cmbAuth.Text == "" && cmbClassi.Text != "")
{
SqlConnection Icon = new SqlConnection(con);
String Query = "SELECT * FROM Books WHERE Classification ='" + cmbClassi.Text + "'"; ;
SqlCommand Command = new SqlCommand(Query, Icon);
SqlDataAdapter da = new SqlDataAdapter(Command);
DataTable dt = new DataTable();
da.Fill(dt);
BrDataGrid.DataSource = dt;
}
if (cmbAuth.Text != "" && cmbClassi.Text == "")
{
SqlConnection Icon = new SqlConnection(con);
String Query = "SELECT * FROM Books WHERE Author ='" + cmbAuth.Text + "'"; ;
SqlCommand Command = new SqlCommand(Query, Icon);
SqlDataAdapter da = new SqlDataAdapter(Command);
DataTable dt = new DataTable();
da.Fill(dt);
BrDataGrid.DataSource = dt;
}
}
This logic:
if (cmbAuth.Text == "" && cmbClassi.Text != "")
{
// build and execute the entire query
}
would lead to an exponential increase in the number of if blocks and a ton of repeated code. Which, of course, is bad.
Instead, build the query once with a linear increase in the number of filters applied. Something logically more like this:
var query = "SELECT * FROM Books WHERE 1 = 1";
var parameters = new List<SqlParameter>();
if (!string.IsNullOrEmpty(cmbAuth.Text))
{
query = query + " AND Author = #Author";
parameters.Add(new SqlParameter("#Author", cmbAuth.Text));
}
// repeat for each input
// then build and execute the connection/command objects from the query and parameters
Basically you build up the query with each condition, then execute the result. As a bonus, this is also using query parameters rather than the current SQL-injectable code you have. You can further add slight improvements to this, such as using a StringBuilder instead of concatenating strings or perhaps using more explicit SQL data types when building the parameter objects, etc.
This would be similar to a commonly used approach in LINQ, where one might have something like this:
var books = db.Books;
if (!string.IsNullOrEmpty(cmbAuth.Text))
books = books.Where(b => b.Author == cmbAuth.Text);
if (!string.IsNullOrEmpty(cmbClassi.Text))
books = books.Where(b => b.Classification == cmbClassi.Text);
// etc.
I am trying to bind sql data on textboxes against reading data from label my code is as below:
string sql1 = " select openbal from AccountMast where accname='" + comboBox1.Text + "' and companyID='" + label4.Text + "'";
SqlDataAdapter dap1 = new SqlDataAdapter(sql1, con);
DataSet ds1 = new DataSet();
dap1.Fill(ds1);
for (int p = 0; p < ds1.Tables[0].Rows.Count; p++)
{
if (label11.Text == "Dr")
{
txtopenbaldr.Text = Convert.ToString(ds1.Tables[0].Rows[p]["openbal"]);
}
if (label11.Text == "Cr")
{
txtopenbalcr.Text = Convert.ToString(ds1.Tables[0].Rows[p]["openbal"]);
}
}
//Label11 Bind by Sql.
string sql10 = " select obcat from AccountMast where accname='" + comboBox1.Text + "' and companyID='" + label4.Text + "'";
SqlDataAdapter dap10 = new SqlDataAdapter(sql10, con);
DataSet ds10 = new DataSet();
dap10.Fill(ds10);
for (int p = 0; p < ds10.Tables[0].Rows.Count; p++)
{
label11.Text = Convert.ToString(ds10.Tables[0].Rows[p]["obcat"]);
}
The label11 bound by sql data and it should display text "Dr" OR "Cr" at a time.
but it's not working as the label11.text not support for bind the data onto textboxes
I have two textboxes as below:
Opening Balance/Debit Opening Balance/Credit
txtopenbaldr.Text txtopenbalcr.Text
There are two textboxes which can databind on above condition: Remember only one textbox should be bind as per condition.
I am trying the trick but it's fail. Suggest the solution.
I'm assuming that you simply appended the code for label11.text at the end of your message, but that in reality label11.text is assigned before you try to set txtopenbaldr.Text or txtopenbalcr.Text.
If that's the case, I would make sure that label11.Text actually has the value Dr or Cr, and not DR or CR, as the comparisons will be case-sensitive.