I want to create excel file by oledb, and my code is
int index = 0;
foreach (DataColumn col in dt.Columns)
{
datatype[index] = $"[{col.ColumnName}]" + " String";
index++;
}
string query = string.Join(",", datatype);
cmd.CommandText = $"CREATE TABLE [{sheetName}] ({query});";
cmd.ExecuteNonQuery();
and I have column name like Product material[EN]
I used break point and check my command string
CREATE TABLE [products]
(
[another Title] String,
[Use of product[EN]] String,
[Product material[EN]] String,
[another Title] String
);
I've tried
Product material[EN]
Product material[[EN]]]
[Product material[EN]]
'[Product material[EN]]'
Product material[[]EN]
['Product material[EN]']
let all columns in query without bracket
CREATE TABLE [products]
(
another Title String,
Use of product[EN] String,
Product material[EN] String,
another Title String
);
let all value in query without bracket
CREATE TABLE [products]
(
another Title String,
Use of product EN String,
Product material EN String,
another Title String
);
but the above syntax all received Syntax error in field definition.
If using parameter and modify the code
cmd.CommandText = $"CREATE TABLE [{sheetName}] (";
for (int i = 0; i < dt.Columns.Count; i++)
{
cmd.CommandText = cmd.CommandText + "[#var" + i.ToString() + "] String,";
cmd.Parameters.AddWithValue("#var" + i.ToString(), datatype[i]);
}
cmd.CommandText = cmd.CommandText.Remove(cmd.CommandText.Length - 1, 1) + ");";
or change
cmd.Parameters.AddWithValue("#var" + i.ToString(), datatype[i]);
to
cmd.Parameters.Add(new OleDbParameter("#var" + i.ToString(), datatype[i]));
and get the command string
CREATE TABLE [products]
(
[#var0] String,
...
[#varN] String
);
will get a successful excel file but with #varxx column names
change to Product material(EN) is work but I need to use bracket []
how do I add bracket into column name use oledb ?
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 am trying to create dynamical insert statements from text file, with only SQLite.
What I have done so far, is to create the SQL query with necessary parameters, add those parameters during run time, and try to select.
However I get error inside try block, when try to cmd.ExecuteNonQuery();
Caught exception: SQL logic error or missing database
near "#0": syntax error
using System;
using System.Data.SQLite;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data.SqlClient;
namespace TestApp
class Program
{
static void Main(string[] args)
{
//To store the index and column name in the file
Dictionary<int, string> Columns = new Dictionary<int, string>();
char[] delimiterChars = { '\t' };
string createQuery = #" create table if not exists products(id integer not null primary key, name text);
insert into products (name) values ('A');
insert into products (name) values ('B');
insert into products (name) values ('C');
insert into products (name) values ('D');
insert into products (name) values ('E');
insert into products (name) values ('F');
insert into products (name) values ('G');
create table if not exists orders(id integer, dt datetime, product_id integer, amount real);";
System.Data.SQLite.SQLiteConnection.CreateFile("myDB.db3");
using (System.Data.SQLite.SQLiteConnection conn = new System.Data.SQLite.SQLiteConnection("data source=myDB.db3")){
using (System.Data.SQLite.SQLiteCommand cmd = new System.Data.SQLite.SQLiteCommand(conn)){
conn.Open();
cmd.CommandText = createQuery;
cmd.ExecuteNonQuery();
string[] lines = System.IO.File.ReadAllLines(Directory.GetCurrentDirectory() + #"../../../App_Data/import.txt");
cmd.CommandText = "INSERT INTO orders (";
// Identify the column order from first row of the import file
string[] elements = lines[0].Split(delimiterChars);
for (int i = 0; i < elements.Length; i++)
{
Columns[i] = elements[i];
cmd.CommandText = cmd.CommandText + "#COLUMN" + i + ", ";
cmd.Parameters.AddWithValue("#COLUMN" + i, Columns[i]);
System.Console.WriteLine(i + " : " + Columns[i]);
}
cmd.CommandText = cmd.CommandText.Remove(cmd.CommandText.Length - 2);
cmd.CommandText = cmd.CommandText + ") VALUES (";
string temp = cmd.CommandText;
System.Console.WriteLine(cmd.CommandText);
System.Console.WriteLine("Contents of Import File.txt = ");
for (int i = 1; i < lines.Length; i++)
{
cmd.CommandText = temp;
elements = lines[i].Split(delimiterChars);
for (int j = 0; j < elements.Length; j++)
{
cmd.CommandText = cmd.CommandText + "#VALUE" + j + ", ";
cmd.Parameters.AddWithValue("#VALUE" + j, elements[j]);
}
cmd.CommandText = cmd.CommandText.Remove(cmd.CommandText.Length - 2);
cmd.CommandText = cmd.CommandText + ")";
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine("Caught exception: " + ex.Message);
}
Console.WriteLine(cmd.CommandText);
Console.WriteLine(lines[i]);
}
cmd.CommandText = "Select * from orders";
cmd.ExecuteNonQuery();
using (System.Data.SQLite.SQLiteDataReader reader = cmd.ExecuteReader()){
while (reader.Read())
{
Console.WriteLine(reader["ID"] + " | " + reader["dt"] + " | " + reader["product_id"] + " | " + reader["amount"]);
}
conn.Close();
}
}
}
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
}
I am not sure on what am I doing wrong?
The import.txt file consists of
id dt amount product_id
1 2017-02-01T10:01:12 343.33 1
2 2017-02-01T10:02:12 12 2
3 2017-03-01T10:03:12 344.3 1
4 2017-04-01T10:04:12 12 3
5 2017-05-01T10:05:12 66.5 1
6 2017-06-01T10:06:12 4
All items divided by TAB
The loop over the column names is useless because you already know the column names from the CREATE TABLE ORDERS command executed at the first lines.
By the way, you cannot use parameters to express the name of a column.
This is not allowed in any kind of database system that I know of.
You can safely remove it but note that you have declared the column order wrongly. In the CREATE TABLE you have
create table if not exists orders(
id integer, dt datetime, product_id integer, amount real
while in the file the product_id is the last column. So you need to adapt your CREATE TABLE to your file
create table if not exists orders(
id integer, dt datetime, amount real, product_id integer;
Next your insertion loop code could be rewritten in this way (ignoring the variable number of arguments as you explain)
string baseQuery = "INSERT INTO orders (id, dt, amount, product_id ) VALUES(";
string[] lines = System.IO.File.ReadAllLines(#"e:\temp\orders.txt");
// Skip the first line
for (int i = 1; i < lines.Length; i++)
{
string[] elements = lines[i].Split(delimiterChars);
// Keep the parameter names in a list to get an easy way to
// concatenate them all together at the end of the loop
List<string> text = new List<string>();
for (int j = 0; j < elements.Length; j++)
{
text.Add("#VALUE" + j);
cmd.Parameters.AddWithValue("#VALUE" + j, elements[j]);
}
// Create the command text in a single shot
cmd.CommandText = baseQuery + string.Join(",", text) + ")";
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine("Caught exception: " + ex.Message);
}
}
Consider also to enclose your database code inside a transaction like this as suggested by the link posted below by Alexander Petrov
using (System.Data.SQLite.SQLiteConnection conn = new System.Data.SQLite.SQLiteConnection("data source=myDB.db3"))
using (System.Data.SQLite.SQLiteCommand cmd = new System.Data.SQLite.SQLiteCommand(conn))
{
conn.Open();
using(SQLiteTransaction tr = conn.BeginTransaction())
{
cmd.Transaction = tr;
.....
for(....)
{
.....
}
tr.Commit();
}
}
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;
}
}
in C# project , I have made a datagridview in my form that has some columns.column[0]& column[1] names are fix (day and date) and the other column names are variable and will change by user.these columns have time period thatsis shown with starting and finishing time.
such as from 6 am to 14 pm is shown as 6_14 as columns name.we have a listbox that has variable number of items(counter "i").
///sqlite doesn't accept columns name that start and finish with Numbers.I added word "f" to start and end of each
///column name
string data_str = "";
string data_str2="";
for (int i = 0; i < listBox1.Items.Count;i++ )
{
string temp = "f"+ listBox1.Items[i].ToString()+"f";
data_str = data_str + temp +",";
string temp2 = "#"+listBox1.Items[i].ToString()+",";
data_str2=data_str2+temp2;
}
data_str=data_str.TrimEnd(',');
data_str2=data_str2.TrimEnd(',');
in first step,I made two columns "day" & "date" in data table in my database and imported my data to it successfully.(It works)
string Q_insert = "insert into " + table_name + " (day,date) values (#day,#date)";
SQLiteConnection connect = new SQLiteConnection(connection_string);
SQLiteCommand insert_cmd = new SQLiteCommand(Q_insert, connect);
foreach (DataGridViewRow row in shift_datagrid.Rows)
{
insert_cmd.Parameters.AddWithValue("#day", row.Cells[0].Value.ToString().Trim());
insert_cmd.Parameters.AddWithValue("#date", row.Cells[1].Value.ToString().Trim());
connect.Open();
insert_cmd.ExecuteNonQuery();
connect.Close();
}
in second step,I need to import user data from datagridview to data table but the error takes place.(it doesn't work)
int col_cnt = listBox1.Items.Count;
Q_insert = "insert into " + table_name + " (" + data_str + ") values (" + data_str2 + ")";
connect = new SQLiteConnection(connection_string);
insert_cmd = new SQLiteCommand(Q_insert, connect);
foreach (DataGridViewRow row in shift_datagrid.Rows)
{
string temp1 = "";
for (int i = 0; i < col_cnt; i++)
{
temp1 = "\"#" + listBox1.Items[i].ToString() +"\"";
insert_cmd.Parameters.AddWithValue(temp1, row.Cells[i + 2].Value.ToString().Trim());
}
connect.Open();
insert_cmd.ExecuteNonQuery();
connect.Close();
}
unfortunately, this error occurs:
unknown Error : Insufficient parameters supplied to the command
I googled this error and checked solution ways for same problems but they were not useful for my problem and they couldn't help me anyway.
this is the example of output command strings that insert to data to database (two steps):
1. Insert into [table_name] (day,date) values (#day,#date) -------------->(it works)
2. Insert into [table_name] (f6_14f,f14_22f,f22_6f) values (#6_14,#14_22,#22_6) ------->(it doesn't work)
please help me
thanks
The following may be causing the error:
temp1 = "\"#" + listBox1.Items[i].ToString() +"\"";
insert_cmd.Parameters.AddWithValue(temp1, row.Cells[i + 2].Value.ToString().Trim());
Syntax requires that you don't construct the # as part of the variable value.# is not data. Use it as part of the variable name as you did before like this:
insert_cmd.Parameters.AddWithValue("#Col1",listBox1.Items[i+2].Value.ToString().Trim());
Where #Col1 is a column name of your table.
I don't think you can build the parameter name in a for loop, if so, you have to list the insert statements with each column name parameter prefixed with # as in the above example/code.
I am importing a row in a datagridview from another one for editing.
Edit the values.
Save the values.
But I do not want the user to make changes in the column which is the PRIMARY KEY TABLE.
I have the name of primary key column by
string sql = "SELECT ColumnName = col.column_name FROM information_schema.table_constraints tc INNER JOIN information_schema.key_column_usage col ON col.Constraint_Name = tc.Constraint_Name AND col.Constraint_schema = tc.Constraint_schema WHERE tc.Constraint_Type = 'Primary Key' AND col.Table_name = '" + _lstview_item + "'";
SqlConnection conn2 = new SqlConnection(cc.connectionString(cmb_dblist.Text));
SqlCommand cmd_server2 = new SqlCommand(sql);
cmd_server2.CommandType = CommandType.Text;
cmd_server2.Connection = conn2;
conn2.Open();
string ColumnName = (string)cmd_server2.ExecuteScalar();
//string ColumnName = (string)cmd_server2.ExecuteScalar();
conn2.Close();
and I am building up the update statement as below
u.Append("UPDATE ");
u.Append("[Ref].[");
u.Append(_lstview_item);
u.Append("]");
u.Append("SET ");
for (i = 0; i < col_no; i++)
{
u.Append(col_name);
u.Append(" = ");
u.Append("'");
u.Append(col_value);
u.Append("'");
}
How do i detect if the user is changing the value of the primary key column in this situation ?
You should not concatenate your SQL statement like that.
Instead use SQL parameters to set the individual properties.
Se an example of how SQLParameters are used in this sample:
http://www.dotnetperls.com/sqlparameter
Hope this helps!