The problem:
I'm trying to insert a date time into an access database using the Oledb interface in C#.
Hacking solution: Generate my on insert string without using command.Properties
I can insert text into the database with no problem, but when trying datetime, I end up with this error: System.Data.OleDb.OleDbException {"Data type mismatch in criteria expression."}
There are several posts similar to this but alas with no working solution.
Here is my code:
void TransferData()
{
string instCmd = Get_InsertCommand(0); // hard coded table 0 for testing
Fill_ProductTable_ToInsert();
con.Open();
// It would be nice not to have to separate the date indexes
int[] textIndex = { 0, 1, 2, 3, 4, 7 };
int[] dateIndex = { 5, 6 };
try
{
foreach (DataRow row in DataToStore.Tables[0].Rows)
{
OleDbCommand command = new OleDbCommand();
command.Connection = con;
command.CommandText = instCmd;
foreach(int j in textIndex)
command.Parameters.AddWithValue("#" + j, row[j]);
foreach (int j in dateIndex)
{
// TESTING CODE
///////////////////////////////////////////////////////////////////////////
string input = "#\'" +DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") +"\'#";
command.Parameters.AddWithValue("#" + j, input.ToString());
Program.WriteLine(input.ToString());
///////////////////////////////////////////////////////////////////////////
}
command.ExecuteNonQuery();
}
}
finally
{
con.Close();
}
}
string Get_InsertCommand(int i)
{
string sqlIns = "INSERT INTO " + DataToStore.Tables[0].TableName + " (";
string temp = "VALUES (";
for (int j = 0; j < expected_header[i].Length - 1; j++)
{
sqlIns += expected_header[i][j] + ", ";
temp += "#" + j + ", ";
}
int lastIndex = expected_header[i].Length -1;
sqlIns += expected_header[i][lastIndex] + ") ";
temp += "#" + lastIndex + ")";
sqlIns += temp;
return sqlIns;
}
Inside the area labeled testing code, I have tried every permutation of date time I could think of.
I tried every format with # and '
I tried these formats: yyyy-MM-dd, yyyyMMdd, yyyy\MM\dd, yyyy/MM/dd
I also tried ToOADate()
And ToString(), ToShortDateString()
I also tried setting the database to accept ANSI-92 Sql
I'm running out of ideas.
Note: This code is set up to deal with multiple tables from multiple databases, mind the loops...
Use parameters properly, and don't worry about the format of the datetime value that you concatenate in your query.
I don't understand why you want to convert the datetime value to a string value ?
DateTime theDate = new DateTime(2012,10,16);
var cmd = new OleDbCommand();
cmd.CommandText = "INSERT INTO sometable (column) VALUES (#p_bar)";
cmd.Parameters.Add ("#p_bar", OleDbType.DateTime).Value = theDate;
I was able to solve this issue by not using command properties. I generated my own sql input and set it to cmd.commandText. The text input for datetime to a data base is #yyyy-MM-dd#
Related
everybody,
I want to insert data from a DateGridView table into MySQL. It works so far but as soon as there are decimal numbers then they are not stored as decimal in MySQL but as integers. The decimal places are not transferred. I tried to convert the data but without success. They are not stored as decimal numbers.
Thanks for the tips !!!!
Here is the code
private void button2_Click(object sender, EventArgs e)
{
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++) // dòng
{
string str = "server=test; database=test; uid=test; pwd=test;";
MySqlConnection constr = new MySqlConnection(str);
constr.Open();
String cmdText = "INSERT INTO KPI_Kosten (Betrag, Tower, Jahr, Periode, Land) VALUES ('"
+ Convert.ToDecimal(dataGridView1.Rows[i].Cells[0].Value) + "','"
+ dataGridView1.Rows[i].Cells[1].Value + "','"
+ dataGridView1.Rows[i].Cells[2].Value + "','"
+ dataGridView1.Rows[i].Cells[3].Value + "','"
+ dataGridView1.Rows[i].Cells[4].Value + "' )";
MySqlCommand cmd = new MySqlCommand(cmdText, constr);
cmd.ExecuteNonQuery();
constr.Close();
}
}
Since decimal places are truncated because column data type definition set to integer, first you should modify Betrag column to decimal data type with certain precision and scale like example below:
ALTER TABLE KPI_Kosten MODIFY COLUMN Betrag decimal(10, 2);
Afterwards, try setting MySqlParameter with parameterized query to enforce decimal data type with specified settings (see documentation):
string str = "server=test; database=test; uid=test; pwd=test;";
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
using (MySqlConnection constr = new MySqlConnection(str))
{
constr.Open();
// if '#' prefix for parameters not working, change to '?'
String cmdText = "INSERT INTO KPI_Kosten (Betrag, Tower, Jahr, Periode, Land) VALUES (#Betrag, #Tower, #Jahr, #Periode, #Land)";
using (MySqlCommand cmd = new MySqlCommand(cmdText, constr))
{
// if you're experiencing too many parameters error, uncomment this line below
// cmd.Parameters.Clear();
MySqlParameter betrag = new MySqlParameter("#Betrag", MySqlDbType.Decimal);
betrag.Precision = 10;
betrag.Scale = 2;
betrag.Value = Convert.ToDecimal(dataGridView1.Rows[i].Cells[0].Value);
cmd.Parameters.Add(betrag);
cmd.Parameters.Add("#Tower", MySqlDbType.VarChar).Value = dataGridView1.Rows[i].Cells[1].Value;
// other parameters
cmd.ExecuteNonQuery();
}
}
}
Note: You can also try simplified version without precision like cmd.Parameters.Add("#Betrag", MySqlDbType.Decimal).Value = Convert.ToDecimal(dataGridView1.Rows[i].Cells[0].Value);.
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();
}
}
I have had a problem for a few days and nothing online seems to do it.
I have an SQL table that has 150 columns. I am reading data from an ODBC connection and I want to insert that data into the SQL table. Basically duplicate the ODBC table as SQL.
My problem is that if I put everything in a string and insert it I face a hell of a time with escape characters and exceptions that I can't figure out.
Is there a way to parametrize my insert values that doesn't involve me naming each and every on of them separatly.
This is what I have right now. Also, if anyone knows an easier way to move an ODBC table to an SQL form please let me know
private void fillTable(string tableName)
{
String query = "SELECT * FROM " + tableName;
OdbcCommand command = new OdbcCommand(query, Program.myConnection);
OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
int columnCount = reader.FieldCount;
for (int i = 0; i < columnCount; i++)
{
SqlCommand sCommand = new SqlCommand("ALTER TABLE " + tableName + " ADD " + reader.GetName(i) + " varchar(MAX)", Program.myConnection2);
sCommand.ExecuteNonQuery();
}
string row="";
while (!reader.IsClosed)
{
try
{
row = "";
reader.Read();
for (int i = 0; i < columnCount; i++)
{
if (reader != null)
{
if (reader.GetString(i).Contains('\''))
{
Console.WriteLine("REPLACED QUOT");
String s = reader.GetString(i).Replace("\'", "A");
Console.WriteLine(s);
row += s;
}
else
{
row += "\'" + reader.GetString(i).Trim() + "\',";
}
// Console.WriteLine("FILLER: " + reader.GetString(i));
}
//Console.WriteLine(row);
}
//Console.WriteLine();
row = row.Substring(0, row.Length - 1);
SqlCommand insertCommand = new SqlCommand("INSERT INTO " + tableName + " VALUES(\'1\'," + row + ")", Program.myConnection2);
insertCommand.ExecuteNonQuery();
}
catch (SqlException exp)
{
Console.WriteLine(exp.StackTrace);
Console.WriteLine(row);
// this.Close();
}
}
Program.myConnection2.Close();
}
You can write a method that creats parameter names automatically, adds it to command, and returns the name so that you can use it in the query:
private int _paramCounter = 1;
private string CreateParameter(SqlCommand command, object value) {
string name = "#P" + _paramCounter.ToString();
_paramCounter++;
command.Parameters.AddWithValue(name, value);
return name;
}
Usage:
row += CreateParameter(insertCommand, reader.GetString(i).Trim()) + ",";
Note that you need to create the command object before you loop through the columns. Also, although not needed, you might want to reset the _paramCounter for each row, otherwise the parameter names get longer in the end.
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.
Second question of all time on this community! I am a noob and my weakness are if statements within or amoungst loops and other if statements.
So here is my scenario. This method inserts anything into a database, but I want to validate something. Instead of adding anything into the database, I do not want anything entered in that begins with "LIFT", I want the method to skip over that line and proceed to the next one. Is there a way I can program this into this method? Or do I need to write a new method? Thanks a bunch!
public bool BatchInsert(string table, string[] values)
{
string statement = "INSERT INTO " + table + " VALUES(";
for (var i = 0; i < values.Length - 1; i++)
{
if(values[i].Contains("'")){
values[i] = values[i].Replace("'", "''");
}
statement += "'"+values[i]+"', ";
}
statement += "'" + values[values.Length - 1] + "');";
SqlCommand comm = new SqlCommand(statement, connectionPCICUSTOM);
try
{
comm.Connection.Open();
comm.ExecuteNonQuery();
}
catch (Exception e)
{
KaplanFTP.errorMsg = "Database error: " + e.Message;
}
finally
{
comm.Connection.Close();
}
return true;
}
A couple hints. Don't += string types as it slows down performance. I also prefer foreach loops as the code is cleaner and easier to read/less likely to mess up the index. Also make using of the using statement to ensure proper disposal.
Assuming you have a reference to System.Linq you can use the following. I didn't test it but it should work:
public bool BatchInsert(string table, IEnumerable<string> values)
{
var sql = new StringBuilder();
sql.Append("INSERT INTO " + table + " VALUES(");
var newValues = values.Where(x => !x.StartsWith("LIFT")).Select(x => string.Format("'{0}'", x.Replace("'", "''")));
sql.Append(string.Join("","", newValues.ToArray()));
sql.Append(")");
using (var comm = new SqlCommand(statement, connectionPCICUSTOM))
{
try
{
comm.Connection.Open();
comm.ExecuteNonQuery();
}
catch (Exception e)
{
KaplanFTP.errorMsg = "Database error: " + e.Message;
}
finally
{
comm.Connection.Close();
}
}
return true;
}
If your goal is to iterate through your collection of 'values', leaving values beginning with 'lift' and their corresponding columns untouched, you may have to revise the way your INSERT Query is constructed. You will add columns as needed, instead of assuming that each value will be accounted for. Basically, you will need to use the form:
INSERT INTO tablename (col1, col2...) VALUES (val1, val2...)
For example:
string statement = "INSERT INTO tablename ";
string columns = "(";
string values = "(";
for (var i = 0; i < values.Length - 1; i++)
{
//if values doesn't contain lift, add it to the statement
if(!values[i].contains("LIFT")){
//columnName is a collection of your db column names
columns += "'"+columnName[i]+"'";
values += "'"+values[i]+"'";
}
}
columns += ")";
values += ")";
statement += columns +" VALUES " + values;
Like some of the comments have stated, this approach opens you up to SQL injections. Use with caution.
EDIT : Sorry, I missed where you said 'starts with 'LIFT'. Revise the .contains() line to the following:
if(!values[i].StartsWith("LIFT")){