I'm working in Microsoft Visual C# 2008 Express with Sqlite.
I understand that an apostrope (') in my text has problems in a query. My problem is that I thought I could replace it with \'. It doesn't seem to be working... Here's a parred down example of my code:
string myString = "I can't believe it!";
cmd.CommandText = "Insert into myTable (myid,mytext) values (1,'" + myString.Replace("'","\\'") + "');";
The error I get is:
SQLite error:
near "t": syntax error
I've tried a couple other replacements... like the other slash. And I wrote my string and a replaced version of my string out to the console to make sure it was coming out right.
What stupid error am I making here?
Thanks!
-Adeena
The solution presented by Robert will work (i.e. replacing ' by '').
Alternatively you can use parameters as in:
DbCommand cmd = new DbCommand();
DbParameter param = cmd.CreateParameter();
// ...
// more code
// ...
cmd.CommandText = "Insert table (field) values (#param)";
param.ParameterName = "param"
param.DbType = DbType.String;
param.Value = #"This is a sample value with a single quote like this: '";
cmd.Parameters.Add(param);
cmd.ExecuteNonQuery();
Using parameters protects against sql injection, and makes the ' problems qo away.
It is also much faster because sqlite can reuse the execution plan of statements when you use parameters. It can't when you don't use parameters. In this example using a parameter makes the bulk insert action approximately 3 times faster.
private void TestInsertPerformance() {
const int limit = 100000;
using (SQLiteConnection conn = new SQLiteConnection(#"Data Source=c:\testperf.db")) {
conn.Open();
using (SQLiteCommand comm = new SQLiteCommand()) {
comm.Connection = conn;
comm.CommandText = " create table test (n integer) ";
comm.ExecuteNonQuery();
Stopwatch s = new Stopwatch();
s.Start();
using (SQLiteTransaction tran = conn.BeginTransaction()) {
for (int i = 0; i < limit; i++) {
comm.CommandText = "insert into test values (" + i.ToString() + ")";
comm.ExecuteNonQuery();
}
tran.Commit();
}
s.Stop();
MessageBox.Show("time without parm " + s.ElapsedMilliseconds.ToString());
SQLiteParameter parm = comm.CreateParameter();
comm.CommandText = "insert into test values (?)";
comm.Parameters.Add(parm);
s.Reset();
s.Start();
using (SQLiteTransaction tran = conn.BeginTransaction()) {
for (int i = 0; i < limit; i++) {
parm.Value = i;
comm.ExecuteNonQuery();
}
tran.Commit();
}
s.Stop();
MessageBox.Show("time with parm " + s.ElapsedMilliseconds.ToString());
}
conn.Close();
}
}
Sqlite behaves similar to Oracle when it comes to the importance of using parameterised sql statements.
Related
I'm fairly new to SQL and trying to figure out the best way to add some predefined data. I figured out from searching around here that I should used a parameterized command to avoid a sql injection attack which isn't a huge concern in this case but I would like to avoid the possibility and learn to do it right... Anyway here is the code I have right now:
using (SqlTransaction trans = connection.BeginTransaction())
{
foreach (IEnumerable<string> row in table.RowData)
{
using (SqlCommand sql = new SqlCommand("INSERT INTO " + table.Title
+ " (" + string.Join(", ", table.Headers)
+ ") VALUES (" + string.Join(", ", table.Headers.Select(x => "#" + x)) + ");", connection, trans))
{
for (int i = 0; i < table.Headers.Count(); i++)
{
if (string.IsNullOrEmpty(row.ElementAt(i)))
{ sql.Parameters.AddWithValue("#" + table.Headers.ElementAt(i), DBNull.Value); }
else
{ sql.Parameters.AddWithValue("#" + table.Headers.ElementAt(i), row.ElementAt(i)); }
}
sql.ExecuteNonQuery();
}
}
trans.Commit();
}
This seems to work and all the data gets in there but it 'feels' inefficient to me. I'm wrapping it in a transaction so there is only one commit, but it's creating the parameters every time and just setting different values for each row.
Is there a way to make this use the same parameters but just set different values per row? Or is this the best way to do this and I should not worry about it?
Thanks in advance for any help you can give.
We can do what you want by parsing the headers into parameters in a pre-processing step. I have also removed the explicit transaction because every single insert already gets an implicit transaction by default (why pay the performance penalty of two transactions?).
using (var command = new SqlCommand()) {
command.CommandText =
"INSERT INTO " + table.Title + " ("
+ string.Join(", ", table.Headers)
+ ") VALUES ("
+ string.Join(", ", table.Headers.Select(x => "#" + x))
+ ");";
command.Connection = connection;
foreach (var header in table.Headers) {
/*
Add all parameters as strings. One could choose to infer the
data types by inspecting the first N rows or by using some sort
of specification to map the types from A to B.
*/
command.Parameters.Add("#" + header, typeof(string));
}
foreach (var row in table.RowData) {
for (var i = 0; i < table.Headers.Count(); i++) {
if (!string.IsNullOrEmpty(row.ElementAt(i))) {
command.Parameters["#" + table.Headers.ElementAt(i)].Value = row.ElementAt(i);
}
else {
command.Parameters["#" + table.Headers.ElementAt(i)].Value = DBNull.Value;
}
}
command.ExecuteNonQuery();
}
}
this is my example of insert that works for me
private void insertWordCount(string songId, string wordId, int wordCount)
{
string query = "insert into songs_words_conn values(#wordId,#songId,#wordCount)";
SqlCommand cmd = new SqlCommand(query, conn);
cmd.Parameters.AddWithValue("#wordId", wordId);
cmd.Parameters.AddWithValue("#songId", songId);
cmd.Parameters.AddWithValue("#wordCount", wordCount);
cmd.ExecuteNonQuery();
}
Yes, you can be much more effecient by reusing SqlParameter objects. Here is some pseudo code:
const string sql = "INSERT INTO table1 (column1) VALUES (#p0)";
using (var sqlCommand = new SqlCommand(sql, connection, transaction))
{
var param1 = sqlCommand.Parameters.Add("#p0", SqlDbType.Int);
foreach (var row in table)
{
param1.Value = row["value"];
sqlCommand.ExecuteNonQuery();
}
}
I have written to append functions that insert data from custom c# list into MSAccess.
The first simply sets up a new connection for each individual recordset:
public static void appenddatatotable(string connectionstring, string tablename, string[] values)
{
var myconn = new OleDbConnection(connectionstring);
var cmd = new OleDbCommand();
cmd.CommandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(#RunDate, #ReportingGroup, #Tariff, #Year)";
cmd.Parameters.AddRange(new[] { new OleDbParameter("#RunDate", values[0]), new OleDbParameter("#ReportingGroup", values[1]), new OleDbParameter("#Tariff", values[2]), new OleDbParameter("#Year", values[3])});
cmd.Connection = myconn;
myconn.Open();
cmd.ExecuteNonQuery();
myconn.Close();
}
I then simply loop over my list of values and call this function on each iteration. This works fine but is slow.
In the second function I tried to include the loop in the function and work with BeginTransction and Committransaction:
public static void appenddatatotable2(string connectionstring, string tablename, string datstr, List<PowRes> values)
{
var myconn = new OleDbConnection(connectionstring);
int icounter = 0;
var cmd = new OleDbCommand();
OleDbTransaction trans = null;
cmd.Connection = myconn;
myconn.Open();
foreach (var item in values)
{
if (icounter == 0)
{
trans = cmd.Connection.BeginTransaction();
cmd.Transaction = trans;
}
cmd.CommandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(#RunDate, #ReportingGroup, #Tariff, #Year)";
if (string.IsNullOrEmpty(item.yr))
item.yr = "";
cmd.Parameters.AddRange(new[] { new OleDbParameter("#RunDate", datstr), new OleDbParameter("#ReportingGroup", item.RG), new OleDbParameter("#Tariff", item.tar), new OleDbParameter("#Year", item.yr)});
cmd.ExecuteNonQuery();
icounter++;
if (icounter >= 500)
{
trans.Commit();
icounter = 0;
}
}
if (icounter > 0)
{
trans.Commit();
}
myconn.Close();
}
This also works fine but is EVEN slower.
Is my code wrong? How could I speed up the multiple inserts?
Thanks!
did not test, just my guess for your second function: you add too many parameters to the same command over the loop - cmd.Parameters were never cleared before each usage..
normally committing large set of commands within one connection is much faster than doing them one by one at single connection.
another way to speed up your inserts is to dump all your insert statements into a long text, separated with semicolon, and then fire a commit in one go (i am not sure whether msAccess supports it or not)
EDIT:
to combine the update command into one text:
var updates = values.Select(x => string.Format("INSERT INTO myTable ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES({0}, {1}, {2}, {3})",
datstr, x.RG, x.tar, x.yr))
.Aggregate((m, n) => m + ";" + n);
cmd.CommandText = update;
Though this could have sql injection issues.
this should be significantly faster than all of your exiting versions
public static void appenddatatotable2(string connectionstring, string tablename, string datstr, List<PowRes> values)
{
string commandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(#RunDate, #ReportingGroup, #Tariff, #Year)";
using (var myconn = new OleDbConnection(connectionstring))
{
myconn.Open();
using (var cmd = new OleDbCommand())
{
foreach (var item in values)
{
cmd.CommandText = commandText;
cmd.Parameters.Clear();
cmd.Parameters.AddRange(new[] { new OleDbParameter("#RunDate", datstr), new OleDbParameter("#ReportingGroup", item.RG), new OleDbParameter("#Tariff", item.tar), new OleDbParameter("#Year", item.yr) });
cmd.Connection = myconn;
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
}
}
I have this SQL that works if i just execute on Oracle SQL Developer:
SELECT * FROM MYTABLE
WHERE LOWER(TRANSLATE(DESCRIPTION, 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc'))
LIKE LOWER(TRANSLATE('%são paulo%', 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc'))
But when is execute on C# code, wont work. The result always is 0.
string translate = "'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc'";
string query = string.Format("SELECT * FROM {0}
WHERE LOWER(TRANSLATE(DESCSITE, {2}))
LIKE LOWER(TRANSLATE({1}, {2}))",
TABLE, string.Format("'%{0}%'", str.ToLower()), translate);
UPDATE
This is how show in the breakpoint:
SELECT * FROM PROD
WHERE TRANSLATE(LOWER(DESCSITE), 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc')
LIKE TRANSLATE(LOWER('%macarrão%'), 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc')
And the same problem. Works on Oracle SQL Developer bu wont on C# code.
UPDATE
I tried this, but but in this case is not working to.
string query = string.Format("SELECT * FROM {0}
WHERE LOWER(TRANSLATE(DESCSITE, {1}))
LIKE LOWER(TRANSLATE(:DESCSITE, {1}))", TABLE, translate);
List<OracleParameter> parameters = new List<OracleParameter>();
parameters.Add(new OracleParameter(":DESCSITE", string.Format("'%{0}%'", str)));
If I only try this, i know tha will work, but I have to check the others things.
string query = string.Format(#"SELECT * FROM {0} WHERE CODIPROD = :CODIPROD", TABLE);
List<OracleParameter> parameters = new List<OracleParameter>();
parameters.Add(new OracleParameter(":CODIPROD", id));
UPDATE
I'm using for retun:
OracleCommand command;
command.ExecuteReader();
UPDATE
I tried put Unicode=True on the ConnectionString but nothing
UPDATE
This is how I execute the query. Everything works fine with characters without accents:
OracleConnection connection = new OracleConnection();
connection.Open();
OracleTransaction transaction;
transaction = connection.BeginTransaction();
OracleCommand command;
command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = commandText;
OracleParameter parameter;
command.Parameters.Add(parameter);
reader = command.ExecuteReader();
while (reader.Read())
{
// Get data
}
So, the last try (yesterday 5 pm) I made this:
String x = "SELECT * FROM PROD WHERE TRANSLATE(LOWER(DESCSITE), 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc') LIKE LOWER(TRANSLATE('%"+str+"%', 'âáàãêéèîíìôóòõûúùç', 'aaaaeeeiiioooouuuc'))";
And works fine. But this way I know that is not right.
You are calling the functions in the wrong order. The TRANSLATE function is case-sensitive. Therefore you must make the strings lower case before translating.
SELECT * FROM {0}
WHERE TRANSLATE(LOWER(DESCSITE), {2}) LIKE TRANSLATE({1}, {2})
Also, the second LOWER is superfluous, since you do with str.ToLower() already.
UPDATE
It is still not clear how you really execute the query. Here is an example of how it can be done
string connectionString = "...";
string query = "...";
using (var connection = new OracleConnection(connectionString)) {
var command = new OracleCommand(query);
command.Parameters.Add(":DESCSITE", OracleType.NVarChar);
connection.Open();
using (OracleDataReader reader = command.ExecuteReader()) {
int descSiteOrdinal = reader.GetOrdinal("DESCSITE");
while (reader.Read()) {
Console.WriteLine(reader.GetString(descSiteOrdinal));
}
}
}
I've resolved it adding this in my connectionString like this (Unicode=true;):
protected string conexionOraclePruebas = #"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=xxx)(HOST=xxx)(PORT=xxx)))(CONNECT_DATA=(SERVER=xxx)(SERVICE_NAME=xxx)));"
+ " User Id=xxx;Password=xxx;Min Pool Size=x;Connection Lifetime=x; "
+ " Unicode=true;";
My query was:
query += " AND translate(UPPER(" + field + "), 'ÁÉÍÓÚÀÈÌÒÙÃÊÎÕÛÂÄËÏÖÔÜÇÑ', 'AEIOUAEIOUAEIOUAAEIOOUCN') "
+"LIKE translate(UPPER('%" + this.value.ToString() + "%'), 'ÁÉÍÓÚÀÈÌÒÙÃÊÎÕÛÂÄËÏÖÔÜÇÑ', 'AEIOUAEIOUAEIOUAAEIOOUCN')";
I'm trying to call a simple stored procedure in c# 2010.
With only a IN argument it's ok, but now with a OUT argument it's not working.
In phpmyadmin :
drop procedure if exists insert_artist;
delimiter $$
create procedure insert_student(IN name VARCHAR(100), OUT id INT)
begin
insert into student(name) values(name);
set id = last_insert_id();
end$$
delimiter ;
Then using
call insert_student("toto",#id);
select #id;
It's working fine.
Now, in c# :
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
using (MySqlCommand command = connection.CreateCommand())
{
command.CommandText = "insert_student";
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#name", "xxxx");
command.Parameters.AddWithValue("#id",MySqlDbType.Int32);
command.ExecuteNonQuery();
Console.WriteLine("**** " + command.Parameters["#id"].Value);
}
}
Gives me an exception when executing ExecuteNonQuery() :
OUT or INOUT argument 2 for routine insert_student is not a variable or NEW pseudo-variable in BEFORE trigger
The same thing without the out argument in the stored procedure is working fine.
Where is my mistake?
A fuller example:
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(nameOfStoredRoutine, connection);
cmd.CommandType = CommandType.StoredProcedure;
//input parameters
for (int i = 0; i < (parameterValue.Length / 2); i++)
{
cmd.Parameters.AddWithValue(parameterValue[i, 0], parameterValue[i, 1]);
cmd.Parameters[parameterValue[i, 0]].Direction = ParameterDirection.Input;
parameterList = parameterList + parameterValue[i,0] + " " + parameterValue[i,1] + " ";
}
//single output parameter
cmd.Parameters.AddWithValue("#output", MySqlDbType.Int32);
cmd.Parameters["#output"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery(); //Execute command
this.CloseConnection(); //close connection
return Convert.ToInt32(cmd.Parameters["#output"].Value.ToString());
my below code works
pls check if it's ok for you.
InsertQuery = New MySqlCommand("xxxxxx")
InsertQuery.Connection = Connection
InsertQuery.CommandType = Data.CommandType.StoredProcedure
InsertQuery.Parameters.AddWithValue("IN_xxx", str_xxxx)
InsertQuery.Parameters.Add("OUT_LastID", MySqlDbType.Int32).Direction = ParameterDirection.Output
IQ = InsertQuery.ExecuteReader()
IQ.Read()
LASTID = InsertQuery.Parameters("OUT_LastID").Value
I am trying to insert a record and get its newly generated id by executing two queries one by one, but don't know why its giving me the following error.
Object cannot be cast from DBNull to other types
My code is as below: (I don't want to use sql stored procedures)
SqlParameter sqlParam;
int lastInsertedVideoId = 0;
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlInsertValues = "#Name,#Slug";
string sqlColumnNames = "[Name],[Slug]";
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlParam = sqlCmd.Parameters.Add("#Name", SqlDbType.VarChar);
sqlParam.Value = txtName.Text.Trim();
sqlParam = sqlCmd.Parameters.Add("#Slug", SqlDbType.VarChar);
sqlParam.Value = txtSlug.Text.Trim();
sqlCmd.ExecuteNonQuery();
//getting last inserted video id
sqlCmd.CommandText = "SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
using (SqlDataReader sqlDr = sqlCmd.ExecuteReader())
{
sqlDr.Read();
lastInsertedVideoId = Convert.ToInt32(sqlDr["lastInsertedVideoId"]);
}
}
}
//tags insertion into tag table
if (txtTags.Text.Trim().Length > 0 && lastInsertedVideoId > 0)
{
string sqlBulkTagInsert = "";
string[] tags = txtTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
foreach (string tag in tags)
{
sqlBulkTagInsert += "INSERT INTO tags(VideoId, Tag) VALUES(" + lastInsertedVideoId + ", " + tag.Trim().ToLowerInvariant()+ "); ";
}
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlQuery = sqlBulkTagInsert;
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlCmd.ExecuteNonQuery();
}
}
}
And also if possible, please check is the above code coded well or we can optimize it more for improve performance?
Thanks
The call to SCOPE_IDENTITY() is not being treated as being in the same "scope" as the INSERT command that you're executing.
Essentially, what you need to do is change the line:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
to:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + "); SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
and then call
int lastVideoInsertedId = Convert.ToInt32(sqlCmd.ExecuteScalar());
instead of .ExecuteNonQuery and the code block following the "//getting last inserted video id" comment.
The SCOPE_IDENTITY() should be extracted from the first command (SELECT, RETURN or OUT) and passed into the next command. By that, I mean that the SELECT_IDENTITY() should be at the end of the first command. In SQL 2008 there is additional syntax for bring values back as part of the INSERT, which makes this simpler.
Or more efficiently: combine the commands into one to avoid round-trips.