I have the following code which iterates through a CSV file and writes to the SQL table:
foreach (string line in lines.Skip(1))
{
var sqlfmt = "INSERT INTO [" + tab + "] values ({0})";
var insert = string.Format(sqlfmt, line.Replace("\"", "'"));
MessageBox.Show(insert + "");
SqlCommand ty = new SqlCommand(insert, myConnection);
ty.ExecuteNonQuery();
}
The issue I have now is if one of the column has ' in the text my application crashes.
What would be the best way to avoid the issue?
Just change
var insert = string.Format(sqlfmt, line.Replace("\"", "'"));
To:
var insert = string.Format(sqlfmt, line.Replace("'", "''").Replace("\"", "'"));
The reason for this is that in T-SQL (SQL Server's version of SQL), single-quotes inside a string are escaped by another single quote. For example, if you wanted to properly quote the string Bob's quotes', the properly-escaped SQL string would be 'Bob''s quotes'''.
Related
I would like to do the same thing as oracle sqldeveloper do with parametered queries (but from c#, not java).
Let's say there's an arbitrary, user supplied query, eg
select * from dual where 1 = :parameter
My task is to parse safely similar strings, identify the parameters, ask them from user and execute the query.
Which is the right / safe approach? I guess, there's some oracle client api to do this. Or is the right way using some pl/sql stuff (eg. from DBMS_SQL)? I couldn't find such a thing yet...
Update / clarification: see the example code below:
// user enters the query string with parameters somehow:
string sql = AskUserForSelectString();
// now the value of sql is:
// "select column0 from tablename where column1 = :param1 and column2 = :param2 ;"
// this is my original question: HOW TO DO THIS?
List<string> param_names = OracleParseQueryAndGiveMyParameters(sql);
// param_names is now a list of ":param1",":param2"
// ask user again for parameter values:
var param_values = new List<string>();
foreach (var param_name in param_names)
{
string param_value = AskUserForParameterValue(param_name);
param_values.Add(param_value);
}
// give the parameter values for the query in safe way:
using (var cmd = new SqlCommand(sql, myDbConnection))
{
for (int i=0; i< param_names.Count; i++)
cmd.Parameters.AddWithValue(param_names[i], param_values[i]);
var result = cmd.ExecuteReader();
// process result...
}
The key point is that I don't know the parameters in advance. This is exactly what SqlDeveloper can do.
(That isn't an issue if EF expects # before the parameter name instead of colon, that can be worked out easily.)
You can do it like this:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES (#someValue, #someOtherValue);";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("#someValue", someVariable);
cmd.Parameters.AddWithValue("#someOtherValue", someTextBox.Text);
cmd.ExecuteNonQuery();
}
What you absolutly must NOT do is:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES ('" + someVariable + "', '" + someTextBox.Text + "');";
var cmd = new SqlCommand(sql, myDbConnection);
cmd.ExecuteNonQuery();
The problem with the second example is that it opens your code to an SQL Injection attack.
One (hacky but accurate?!) way with the original ":parameter" bind variable syntax is to call out to C and use Oracle OCI functions to do the parsing for you.
Prepare the statement with OCIStmtPrepare2() and then call
OCIStmtGetBindInfo() to get the variable names.
I'm trying to optimise the code for an insert statement in SQLite.
I have a problem when inserting multiple values into SQLite from C# Application.
If I try this directly into SQL:
INSERT INTO TBL4 (ID,zID,d) VALUES (6144,1,1),(6144,1,2);
the statement writes two rows of data into table name TBL4.
But, if I want to use this statement in c# and execute it, I have some errors getting from database (error is random.)
Here is the code from c#:
//this is just db connection
string connstr = "Data Source = "+frm.basedir+";"+frm.basever+";";
SQLiteConnection conn = new SQLiteConnection(connstr);
conn.Open();
//working code, successful inserted data into SQL
//g and d variable contain values: 6144 and 2
string sql3 = "INSERT INTO tbl3 (id,d) values (" + g + "," + d + ")";
SQLiteCommand comm3 = new SQLiteCommand(sql3, conn);
comm3.ExecuteNonQuery();
//not working code, throws an error
string sql4 = "INSERT INTO tbl4 (ID,zID,d) VALUES (6144,1,1),(6144,1,2)";
SQLiteCommand comm4 = new SQLiteCommand(sql4, conn);
comm4.ExecuteNonQuery();
My SQLitedatabse is 3.8 version.
Thank in advance
I want to retrieve a score of words from databse and then I will make a decision about paragraph that this is positive paragraph or negative paragraph
The database file format is like this. where some key word have positive and negative score
Word Pos_Score Neg_Score
Able .324 .834
Country .987 .213
Love .378 .734
agree .546 .123
industry .289 .714
guests .874 .471
The Paragraph will be like this.
I agree with you. It seems an intelligent tourist industry allows its guests to either immerse fully, in part, or not, depending upon the guest. That is why the ugly American charges have always confused me.
Now I will compare each word of the paragraph with database file if the word found in the database file then I will retrieve the Pos_Scoe and Neg_Score score of word and these score will be store in variable when the whole paragraph will compare at the end Pos_Score will add separately and the Neg_Score will add separately . and this will be the result.
Code that i try is this
private void button1_Click(object sender, EventArgs e)
{
string MyConString = "server=localhost;" +
"database=sentiwornet;" + "password=zia;" +
"User Id=root;";
MySqlConnection connection = new MySqlConnection(MyConString);
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
StreamReader reader = new StreamReader("D:\\input.txt");
string line;
while ((line = reader.ReadLine()) != null)
{
string[] parts = line.Split(' ');
foreach (string part in parts)
{
command.CommandText = "SELECT Pos_Score FROM score WHERE Word = 'part'";
command.CommandText = "SELECT Neg_Score FROM score WHERE Word = 'part'";
//var
connection.Open();
Reader = command.ExecuteReader();
}
}
}
First, this query promises to be horribly inefficient. Instead, if your paragraphs are small enough, I would execute all of the joins inside the database by passing in the arguments as a CSV-list, then converting to a table in SQL. The following function will do that (courtesy of http://codebank.wordpress.com/2007/03/06/simple-sql-csv-to-table-2/):
Caveat: you will need to strip all punctuation out using something like string.Replace(new[] { '.', ',' ... etc })
Also, it's possible that my code doesn't do exactly what you want - it may not even compile - but that is the joy of programming. This gives you the general idea I have on how to solve a rather complex problem.
Edit: I just realized you are using MySql. This code would work for MSSQL - I have never used MySql from the CLR, so I don't know if all of the classes are equivalent. You may need to go back to what you were doing before.
CSV to List
Create Function dbo.fn_CSVToTable (#CSVList Varchar(MAX))
Returns #Table Table (ColumnData Varchar(50))
As
Begin
If right(#CSVList, 1) <> ','
Select #CSVList = #CSVList + ','
Declare #Pos Smallint,
#OldPos Smallint
Select #Pos = 1,
#OldPos = 1
While #Pos < Len(#CSVList)
Begin
Select #Pos = CharIndex(',', #CSVList, #OldPos)
Insert into #Table
Select LTrim(RTrim(SubString(#CSVList, #OldPos, #Pos - #OldPos))) Col001
Select #OldPos = #Pos + 1
End
Return
End
SQL Procedure
CREATE PROCEDURE dbo.spGetWordScores (#csv varchar(MAX))
AS
select POS_SCORE, NEG_SCORE, WORD from score
inner join dbo.fn_CSVToTable(#csv) input
on input.ColumnData = score.WORD
New C# Code
var MyConString = "server=localhost;" +
"database=sentiwornet;" + "password=zia;" +
"User Id=root;";
var connection = new MySqlConnection(MyConString);
//Each line in the array will probably be one paragraph.
var fileLines = File.ReadAllLines("D:\\input.txt");
foreach (var line in fileLines)
{
//Format your line into words by removing punctuation. I'm not going to bother
//with that code because it is trivial.
//var csv = line.Split(' ');
var command = connection.CreateCommand();
command.CommandText = "exec spGetWordScores";
command.Parameters.AddWithValue("#csv", csv);
var ds = command.ExecuteDataSet();
//Now you have a DataSet with your word scores. do with them what you will.
}
Helpful Extension Method
public static class Extensions
{
public static DataSet ExecuteDataSet(this SqlCommand command)
{
using (SqlDataAdapter da = new SqlDataAdapter(command)) {
DataSet ds = new DataSet();
// Fill the DataSet using default values for DataTable names, etc
da.Fill(ds);
return ds;
}
}
}
Going back and forward to the database is going to kill your performance. Best to write a stored procedure that takes in your input string, splits it and calculates the score - this way all of the processing will happen on one machine and you will save significant time by not communicating partial results.
I want to delete multiple records from access database using array.
The array is loaded dynamically from file names.
Then i query the database, and see if the database column values are matching with the array values, if not then delete it, if matches then do not delete it.
the problem is that:
Following is the code that deletes all records irrespective of the where in Condition.
arrays = Directory.GetFiles(sdira, "*", SearchOption.AllDirectories).Select(x => Path.GetFileName(x)).ToArray();
fnames.AddRange(arrays);
here I have use also for loop but that also didnt help me out :( like for(int u = 0; u < arrays.length; u++) { oledbcommand sqlcmd = new oledbcommand ("delete from table1 where name not in ("'+arrays[u]+"')",sqlconnection);
I am using this one currently foreach(string name in arrays)
{
OleDbCommand sqlcmd = new OleDbCommand("delete from table1 where name not in ('" + name + "')", sqlconnection);
sqlcmd.ExecuteNonQuery(); }`
One problem is that your code is confusing.
string [] a = {"" 'a.jpg', 'b.jpg', 'c.jpg' "}
First, you have double " in the beginning,should only be one.
string [] a = {" 'a.jpg', 'b.jpg', 'c.jpg' "}
Then this created a string array with one element,
a[0] = "'a.jpg', 'b.jpg', 'c.jpg'";
Then you do a foreach on this which natuarly ony executes once resulting in this query:
delete from table1 where name not in ('a.jpg', 'b.jpg', 'c.jpg')
But when you load the array dynamically you probably get this array
a[0] = 'a.jpg';
a[1] = 'b.jpg';
a[1] = 'c.jpg';
which will execute 3 times in the foreach resulting in the following 3 queries
delete from table1 where name not in ('a.jpg')
delete from table1 where name not in ('b.jpg')
delete from table1 where name not in ('c.jpg')
After the second one the table will be empty.
You should try this instead:
string[] names = { "a.jpg", "b.jpg","c.jpg","j.jpg" };
string allNames = "'" + String.Join("','", names) + "'";
OleDbCommand sqlcmd = new OleDbCommand("delete from table1 where name not in (" + allNames + ")", sqlconnection);
sqlcmd.ExecuteNonQuery();
Where names is created dynamically ofcause and this will result in the following query matching your test:
delete from table1 where name not in ('a.jpg', 'b.jpg', 'c.jpg')
My preferred way to dynamically fill an array is to use a list instead as a pure array is fixed in size and any change needs to create a new array.
You can loop over a list as eacy as an array.
List<string> names = new List<string>();
//or user var keyword
var names = new List<string>();
Then just use add method to add elements, loop this as needed.
names.Add(filename);
Then for the concatenation:
string allNames = "'" + String.Join("','", names.ToArray()) + "'";
And you are done.
Or you could use
string[] filePaths = Directory.GetFiles(#"c:\MyDir\", "*.jpg");
string[] names = filePaths.ToList().ConvertAll(n => n.Substring(n.LastIndexOf(#"\") + 1)).ToArray();
posting my comment as a answer
your string is not reading in 4 entries, its reading one entry of
string names = " 'a.jpg', 'b.jpg','c.jpg','j.jpg' ";
it should be
string[] names = { "a.jpg", "b.jpg","c.jpg","j.jpg" };
before your for each had a count of 1 now it should have a count of 4 with the actual values
Edit:
Not a lot of effort in this solution i must admit but if you want dynamic input could do something like:
string name = " 'a.jpg', 'b.jpg','c.jpg','j.jpg' ";
string[] names = name.Split(',').Select(x => x.Trim(' ').Trim('\'')).ToArray();
will update later if i get the change as the trims are not good atm
For populating it, if you want it as enumerable a example could be something like
IEnumerable<string> filelocations = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).Select(x => Path.GetFileName(x));
or for string array
string [] lok = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).Select(x => Path.GetFileName(x)).ToArray();
Sounds like you're either not reading the file correctly or the file is empty. You should be checking the array to make sure it's not empty before running the operation against the database. If the array is empty, it SHOULD delete everything in the database as there are no matches.
You need to define the array like this:
string[] names = { "a.jpg", "b.jpg", "c.jpg", "j.jpg" };
the way you are defining the array it contains only one value:
" 'a.jpg', 'b.jpg','c.jpg','j.jpg' "
The code in your comment will delete any files not matching the first file. that would be all non 'a.jpg' files. The next iteration will delete all files that don't match 'b.jpg' which would be 'a.jpg'. This results in an empty table. Edit: The array declaration you have generates a good IN clause when you do it manually, but when you're getting a list of filenames, you're not generating this same string.
You need to perform a join on the array to generate a single string for your where clause that way the where clause includes all files. Your ultimate where clause should look like:
where name not in ('a.jpg','b.jpg','c.jpg','d.jpg')
Right now you have:
where name not in ('a.jpg')
... next iteration
where name not in ('b.jpg')
Also, be mindful that IN operations are expensive and the longer the array is the faster the query grows.
Try using a list if you don't want to create a static sized array
List<string> names = new List<string>();
names.Add("a.jpg");
names.Add("b.jpg");
names.Add("c.jpg");
foreach (string name in names)
{
OleDbCommand sqlcmd = new OleDbCommand("delete from table1
where name not in (" + name + ")",
sqlconnection);
sqlcmd.ExecuteNonQuery();
}
This SELECT finds Kelly as expected:
select [First Name], [Last Name], Phone from [Data$] where [First Name] like "%Kelly%"
In the Excel spreadsheet, the first name is "Kelly" with a capital "K" -- and the SELECT specifies a capital "K" also.
However, if the K in > like "%Kelly%" < is LOWER-case -- like "%kelly%" -- then the record is NOT found. The SELECT is case-sensitive.
SQL Server does not appear to have a lower() or lcase() method that I can apply to the database column (???!!!). Is that actually true? Widespread advice on the net, to append "COLLATE SQL_Latin1_General_CP1_CI_AS" to the SQL statement, produces the error "IErrorInfo.GetDescription failed 0x80004005" when ExecuteReader() is executed.
Can someone please suggest a way to make my SQL SELECT against Excel case-INsensitive?
I've pasted the code below.
(The f.vs() method returns true when passed a Valid String, i.e. one for which IsEmptyOrNull() is false.)
TIA - Hoytster
// The user may specify the first or last names, or category, or
// any combination, possibly all three.
// Build the select; [Data$] is the name of the worksheet
string select = "select [First Name], [Last Name], Phone from [Data$] where ";
if (f.vs(firstName))
select += "[First Name] like \"%" + firstName + "%\" and ";
if (f.vs(lastName))
select += "[Last Name] like \"%" + lastName + "%\" and ";
if (f.vs(category))
select += "[Category] = \"" + category + "\" and ";
select = select.Substring(0, select.Length - 4); // Lose the terminal "and "
// This makes the SQL case-insensitive! BUT IT CAUSES ExecuteReader() FAIL
// select += " [COLLATE SQL_Latin1_General_CP1_CI_AS]";
// Build the connect string, using the specified Excel address file
string connectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
#excelAddressFile +
";Extended Properties=Excel 8.0;";
// Get a DB connection from an OLE DB provider factory
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OleDb");
using (DbConnection connection = factory.CreateConnection())
{
// Assign the connection string
connection.ConnectionString = connectionString;
// Create the DB command
using (DbCommand command = connection.CreateCommand())
{
// Apply the select
command.CommandText = select;
// Retrieve the data -- THIS ExecuteReader() IS WHERE IT FAILS
using (DbDataReader dr = command.ExecuteReader())
{
while (dr.Read())
{
[COLLATE SQL_Latin1_General_CP1_CI_AS] only works in SQL Server. From what I can tell from your question, you're not using SQL Server; you're using an OLEDB data source to query an Excel file, in which case UCASE should work:
if (f.vs(firstName))
select += "UCase([First Name]) like \"%" + firstName.ToUpper() + "%\" and ";
if (f.vs(lastName))
select += "UCase([Last Name]) like \"%" + lastName.ToUpper() + "%\" and ";
SQL Server does have a function called LOWER that will convert a string of characters to all lowercase