Given a SQL query variable, i.e.,
string mySQLQuery = "SELECT TableA.Field1, TableA.Field2,..., TableB.Field1, TableB.Field2,.... FROM TableA LEFT OUTER JOIN TableB ON TableA.Field1 = TableB.Field1"
Is there any straight way I can extract the fields and the table names within the query in two lists? so:
List "Fields":
All fields From table A, table B (and others I could add by joining) with their table prefix (even if there were only one table in a simple 'SELECT * FROM TableA', I'd still need the 'TableA.' prefix).
All fields From table B with their table prefix, by adding them to the list in the usual fieldList.Add() way through looping.
List "Tables":
All tables involved in the query in the usual tablesList.Add() way through looping.
My first approach would be to make a lot of substrings and comparisons, i.e., finding the FROM, then trimming left, the substring until the first blank space, then the JOIN, then trimming, then the first substring until the space..., but that doesn't seem the right way.
REEDIT
I know I can get all the fields from INFORMATION_SCHEMA.COLUMNS with all the properties (that comes later), but the problem is that for that query I need the tables to be known. My steps should be:
The query "SELECT [fields] FROM [tables]" comes from a Multiline Textbox so I can write a SQL Query to fetch the fields I'd like. I take the string by txtMyQuery.Text property.
Find the field in the SELECT query, and find what table belongs to in the FROM clause.
Store the field like [Table].[Field]in a string list List strFields = new List() by the strFields.Add() method;
Then, iterate through the list in a way like:
for (int i = 0; i < fieldList.Count; i++)
{
string mySqlQuery = "SELECT Table_Name, Column_Name, Data_Type FROM INFORMATION_SCHEMA.COLUMNS
WHERE (COLUMN_NAME + "." + TABLE_NAME) ='" + fieldList[i] + "'";
//Commit query, get results in a gridview, etc.
}
Sure,
Tables:
SELECT TABLE_NAME FROM information_schema.TABLES
Fields:
SELECT * FROM information_schema.COLUMNS WHERE TABLE_NAME = N'Your Table'
Ok, after a while, I found SOME way to make this happen... I will work this out as I improve the solution (i.e., now it doesn't work if we use * selections like 'SELECT * FROM TableA', doesn't support aliasing, and all fields in the SELECT should be [table].[field], but will give an idea of what I'm trying to achieve):
This way, I write an SQL statement in a textbox, I pass it onto a new form to check the actual results of the query (if I needed, by clicking on a button "Preview"), and I populate a datagridview with the SCHEMA data I wanted to retrieve.
Thanks to all for your support!
private void btnQuery_Click(object sender, EventArgs e)
{
string strSql = this.txtQuery.Text;
DataTable dt = new DataTable();
String conStr = "Data Source=(LocalDB)\\v11.0;AttachDbFilename=|DataDirectory|\\TestDB.mdf;Integrated Security=True;Connect Timeout=30";
using (SqlConnection conn = new SqlConnection(conStr))
{
//with the call to strSQLSchema, we get the table involved in the query, to retrieve the fields and properties
SqlCommand cmd = new SqlCommand(strSQLSchema(strSql), conn);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
try
{
adapter.Fill(dt);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
this.dgvColumns.DataSource = dt;
}
private void btnPreview_Click(object sender, EventArgs e)
{
//We must pass the sql query to preview
string strSql = this.txtQuery.Text;
SQLQueryDataPreview qp = new SQLQueryDataPreview(strSql);
qp.Show();
}
private string strSQLSchema(string sqlQuery)
{
//we cut the "SELECT " start
sqlQuery = sqlQuery.ToUpper();
sqlQuery = sqlQuery.Substring(7);
//we take all the fields until the FROM
int myIndex = sqlQuery.IndexOf("FROM");
sqlQuery = sqlQuery.Substring(0, myIndex);
sqlQuery = sqlQuery.Trim();
sqlQuery = sqlQuery.Replace(" ", string.Empty);
sqlQuery = sqlQuery.Replace("\r\n", string.Empty);
//Here we add all fields to a list... so far, "*" is not allowed, and all fields should be written [Table].[Name]
string[] myFields = sqlQuery.Split(new char[] {' ', ','});
List <string> myTables = new List<string>();
//We will use this WHERE to find the fields in the SCHEMA. This WHERE first sentence helps to construct a valid where
//and avoid problems with the 'OR' clause in each loop.
string myWhere = "TABLE_NAME + '.' + COLUMN_NAME = ''";
for (int i = 0; i < myFields.Count(); i++)
{
//here we take the table prefix and add it to an array
int tableIndex = myFields[i].IndexOf(".");
if (tableIndex != -1)
{
myTables.Add(myFields[i].Substring(0, tableIndex));
myWhere += "OR (TABLE_NAME + '.' + COLUMN_NAME = '" + myFields[i] + "')";
}
}
//this is a List where we keep the tables derivated from names. We just copy the list generated before with a DISTINCT to eliminate duplicates.
myTables = myTables.Distinct().ToList();
string schema = "SELECT Table_Name, Column_Name, Data_Type FROM INFORMATION_SCHEMA.COLUMNS WHERE " + myWhere;
return schema;
}
}
Related
I got a runtime error saying "Must declare the table variable "#parmTableName". Meaning having table name as sql parameter in the sql-statement is not allowed.
Is there a better option or suggestion than allowing sql injection attack? I don't want to do this C# script for sql statement " DELETE FROM " + tableName + " ";
using(var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM #parmTableName ";
sqlAsk += " WHERE ImportedFlag = 'F' ";
dbCommand.Parameters.Clear();
dbCommand.Parameters.AddWithValue("#parmTableName", tableName);
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}
Go for a white list. There can only be a fixed set of possible correct values for the table name anyway - at least, so I'd hope.
If you don't have a white list of table names, you could start with a whitelist of characters - if you restrict it to A-Z, a-z and 0-9 (no punctuation at all) then that should remove a lot of the concern. (Of course that means you don't support tables with odd names... we don't really know your requirements here.)
But no, you can't use parameters for either table or column names - only values. That's typically the case in databases; I don't remember seeing one which did support parameters for that. (I dare say there are some, of course...)
As others have already pointed out that you can't use Table Name and Fields in Sql Parameter, one thing that you can try is to escape table name using SqlCommandBuilder, like:
string tableName = "YourTableName";
var builder = new SqlCommandBuilder();
string escapedTableName = builder.QuoteIdentifier(tableName);
using (var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM " + escapedTableName; //concatenate here
sqlAsk += " WHERE ImportedFlag = 'F' ";
dbCommand.Parameters.Clear();
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}
(sqlAsk is string, right?) if it's right so let's try this:
using(var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM <table_name> ";
sqlAsk += " WHERE ImportedFlag = 'F' ";
string table_name = "Your table name here"; //<- fill this as u need
sqlAsk = sqlAsk.Replace("<table_name>", table_name); // it will replace <table_name> text to string table_name
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}
I was working on VB.NET and am now switching to C#. I was using the following code to use table as variable from combo box to fill a DataGrid:
Dim strTAB as a String
dtTAB1 = New DataTable
strTAB = cboDTA_TBL.Text
adpPRJ = New SqlDataAdapter("Select * from """ & strTAB & """", conPRJ_NET)
'conPRJ_NET is connection to connect MsSQL Database on server.
adpPRJ.Fill(dtTAB1)
dgFIN_TAB.DataSource = dtTAB1
I am looking for the C# equivalent of """ & strTAB & """.
This code works perfectly in vb.net, no errors.
Can anyone help?
As mentioned, this is a bad design, due to SQL Injection, but here's your answer :
var strTAB = "tableName";
string myString = $"Select * from {strTAB}";
Although, normally you would never concatenate strings to build an Sql statement, you do not need to be concerned about Sql Injection if your combo box DropDownStyle is set to DropDownList. This is essentially "limit to list" but it is NOT the default setting.
The using statements ensure that your database objects are closed and disposed.
I not sure what the the double quotes around the table name are supposed to do but in Sql Server the identifier delimiters are square brackets. ( [ ] )
private void button1_Click(object sender, EventArgs e)
{
string query = "Select * From [" + cboDTA_TBL.Text + "];";
DataTable dtTAB1 = new DataTable();
using (SqlConnection conPRJ_NET = new SqlConnection("Your connection string"))
{
using (SqlDataAdapter adpPRJ = new SqlDataAdapter(query, conPRJ_NET))
{
adpPRJ.Fill(dtTAB1);
}
}
dgFIN_TAB.DataSource = dtTAB1;
}
I use a MySQL command like this:
string db_name= "test";
string db_table = "table";
command.CommandText = "SELECT * FROM " + db_name+ "." + db_table + " WHERE ID = "ID";";
// sometimes you need the: ' around the string-variables
command.CommandText = "SELECT * FROM '" + db_name+ "." + db_table + "' WHERE ID = "ID";";
I am working on my windows form applications. In this winform, I have a checkListBox which binded the data from my sql db. I am trying to match the checkListBox's checkedItem to my sql table column's text which is stored as a nvarchar data type. I ran the debug mode and found out that it skip the entire while loop when the program is executed. I have no idea why because the valuable name items did actually showed which checkbox in checkListBox is checked
This is my code.
foreach(var items in checkListBox1.CheckedItems){
string query = "select * from my_table WHERE employeeName = '"+items+"'"
SqlCommand myCommand = new SqlCommand(query, myConn);
SqlDataReader dr = myCommand.ExecuteReader();
while(dr.Read()){
//read the column
}
}
Here is the screen Shot. I tried to fetch the chineseName in the column (don't worry about what it is lol)
You have multiple problems in your code. You don't need to write your query in ForEach loop. And if you are expecting to get multiple values from your checklistbox then equalto = operator is not your friend, you would need to use IN operator. Now check below example.
private void button1_Click(object sender, EventArgs e)
{
string items = string.Empty;
foreach (var item in checkedListBox1.CheckedItems)
{
if (items.Length == 0)
items = item.ToString();
else
items = items + "," + item;
}
//make myCommand object and open connection on your own
myCommand = new SqlCommand(query, myConn);
string query = #'select distinct firstName, lastName, chineseName, teacherEmail, entryYear, leaveYear, userLoginId, ad.applicationId
from [teacher_detail] as td
LEFT JOIN[class_detail] as cd ON td.teacherId = cd.teacherId
LEFT JOIN[application_teacher] as at ON at.teacherId = td.teacherId
LEFT JOIN[application_detail] as ad ON at.applicationId = ad.applicationId
Where ad.applicationId = 2
and chineseName in (#name)'
myCommand.Parameters.Add("#name", SqlDbType.nvarchar);
myCommand.Parameters["#name"].Value = items;
//now execute query
}
As Data type in database is nvarchar try it by modifying the following statement in your code
string query = "select * from my_table WHERE employeeName = '"+items+"'"
to
string query = "select * from my_table WHERE employeeName = N'"+items.ToString()+"'"
Prefix 'N' is used for the value to compare from checked item
I am writing a function to take as many multiple selected items from a Listbox and pass the vaules to a SQL Query to INSERT values into a table after selecting the filtered values from another table. The code I have typed is below and it doesn't seem to work (the problem is with the way I'm passing the string to the query.
string lbSites = "";
protected void Button1_Click1(object sender, EventArgs e)
{
string cns = "server=abc;database=testDB;Trusted_Connection=True";
using (SqlConnection con = new SqlConnection(cns))
{
using (SqlCommand command = con.CreateCommand())
{
command.CommandText = "INSERT INTO Activity (Hostname,Site,Status,System_Dept,Business_Dept)"
+ "SELECT * FROM Inventory WHERE Site IN ("+lbSites+");"
;
con.Open();
command.Parameters.AddWithValue("#lbSites", lbSites);
command.ExecuteNonQuery();
con.Close();
}
}
}
protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (ListBox1.Items.Count > 0)
{
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected)
{
lbSites += "'" + ListBox1.Items[i].Value + "', ";
}
}
}
}
You should not directly pass values like this to SQL, as it leaves you open to a SQL Injection Attack.
Instead, you can figure out how many parameters you need, and then insert those parameter names into your query.
This approach will work for up to approximately 2,000 values (assuming SQL Server). If you need to pass more values, you will either need to break up the queries into sub-sets, or use parameter-value tables.
Example (not tested, so may have some bugs):
// Get your selected items:
var items = ListBox1.Items.Where(i=>i.Selected).Select(i=>i.Value).ToArray();
// Create a series of parameters #param0, #param1, #param2..N for each value.
string paramNames = string.Join(", ", Enumerable.Range(0,items.Count()).Select(e=>"#param"+e));
// Build the command text and insert the parameter names.
string commandText = "INSERT INTO Activity (Hostname,Site,Status,System_Dept,Business_Dept)"
+ "SELECT * FROM Inventory WHERE Site IN ("+ paramNames +")";
command.CommandText = commandText;
// Now add your parameter values: this binds #param0..N to the values selected.
for(int param=0;param<items.Count();param++)
{
command.Parameters.AddWithValue("#param" + param, items[param]);
}
The value of lbSites is lost everytime you've posted back. Keep it in your ViewState.
Besides, you don't need command.Parameters.AddWithValue("#lbSites", lbSites); since there's no #lbSites parameter in your sql.
try this
SELECT M.REG_NO, T.TYPE_ID
FROM MAIN AS M
INNER JOIN CLASSIFICATION AS C
ON M.REG_NO = C.REG_NO
INNER JOIN TYPE AS T
ON T.TYPE_ID = C.TYPE_ID
WHERE (#Types) like .LIKE '%,' +T.TYPE_ID+ ',%'
I'm creating an application in Visual Studio 2010 C# and MySQL where the user can add, edit, view an employee. I already done with adding and viewing part. However I'm little confused in editing. I have this listView in my form where it displays all the employee added to the database. What I want is that whenever the user will select an employee and click edit button I want the values saved in the database to show in the corresponding textboxes below the listView. Can someone give me any idea how to do this? Please
Screenshot:
Code for listView:
private void getEmployee()
{
listViewEmployee.Items.Clear();
string cmd = "select employee_number, employee_lastname, employee_firstname, employee_middlename, employee_position, employee_datehired from employee";
DBConn db = new DBConn();
DataTable tbl = db.retrieveRecord(cmd);
foreach (DataRow row in tbl.Rows)
{
ListViewItem lv = new ListViewItem(row[0].ToString());
lv.SubItems.Add(row[1].ToString() + ", " + row[2].ToString() + " " + row[3].ToString());
lv.SubItems.Add(row[4].ToString());
lv.SubItems.Add(row[5].ToString());
listViewEmployee.Items.Add(lv);
}
}
private void textBoxSearchEmployee_TextChanged(object sender, EventArgs e)
{
string cmd = "SELECT employee_number, employee_lastname, employee_firstname, employee_middlename, employee_position, employee_datehired FROM employee where employee_lastname Like '" + textBoxSearchEmployee.Text + "%'";
listViewEmployee.Items.Clear();
DBConn db = new DBConn();
DataTable tbl = db.retrieveRecord(cmd);
foreach (DataRow row in tbl.Rows)
{
ListViewItem lv = new ListViewItem(row[0].ToString());
lv.SubItems.Add(row[1].ToString() + ", " + row[2].ToString() + " " + row[3].ToString());
lv.SubItems.Add(row[4].ToString());
lv.SubItems.Add(row[5].ToString());
listViewEmployee.Items.Add(lv);
}
}
It seems to me you are able to populate the listView only.
A few pointers:
1) The way you're writing your SQL statement now is prone to SQL Injection. Use parameters in your SQL commands instead of directly concatenating the variables to your query. See this question for an example on how to do it.
2) Depending on where your other relevant data is located (i.e. if your database is normalized), you might have to do a join in your query.
string sqlQuery = "SELECT * FROM employee
JOIN other_employee_data_table on other_employee_data_table.employeeID = employee.ID
WHERE employee.employee_lastname LIKE #employee_lastname +'%'"
But if your employee table contains all the data, then no need to do a join. Just get all the relevant information from that table.
3) Once you got all the information you need, it's just a matter of reading those data and assigning them to their respective fields.
Pseudo Code:
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
MySqlCommand command = connection.CreateCommand();
command.CommandText = sqlQuery;
command.CommandType = System.Data.CommandType.Text;
// add parameters in this line
using (MySqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
// iterate in each row
for (int i = 0; i < reader.FieldCount; i++)
{
// iterate each column using reader.GetValue(i)
}
}
}
}
When the user presses the "edit" button...
Retrieve the selected employee
Use a SELECT to get the selected employee's information
Populate the text boxes with the selected employee's information
For example,
String employee = listViewEmployee.Text;
String cmd = "SELECT * FROM employee WHERE employee_lastname='" + employee + "'";
DBConn db = new DBConn();
DataTable tbl = db.retrieveRecord(cmd);
txtLastName.Text = tbl.Rows[0][0];
// ...
// etc.
Note: It's a bad idea to concatenate values into a SQL query because if the values are malicious, a different query could be executed. For example, if employee had the value of x' OR '1'='1; DROP TABLE employee; -- or something along those lines, then the employee table could be dropped. The way around this is using Stored Procedures or parameterized queries.