I am building a query string like this.
string query = "SELECT * FROM " + table + " where DATE(Date) > " + howFarBack.ToString("yyyy-MM-dd");
Hwowever, when it executes
while (dataReader.Read())
I am seeing Dates well before the howFarBack ????
public List<OHLC> Select(string table, System.DateTime howFarBack)
{
string query = "SELECT * FROM " + table + " where DATE(Date) > " + howFarBack.ToString("yyyy-MM-dd");
//Create a list to store the result
var list = new List<OHLC>();
//Open connection
if (OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//Create a data reader and Execute the command
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the list
while (dataReader.Read())
{
var ohlc = new OHLC();
ohlc.Date = (System.DateTime)dataReader[0];
ohlc.Open = Math.Round((double)dataReader[1], 2);
When in doubt, try to debug by examining the resulting SQL query, not the C# code that formats the SQL query.
I would guess that your query lacks single-quote delimiters around the date literal. So it is ultimately a query like:
SELECT * FROM MyTable where DATE(Date) > 2021-11-02
But 2021-11-02 isn't a date, it's an arithmetic expression that evaluates to an integer: 2021 minus 11 minus 2 = 2008. This will certainly match a lot of dates you didn't intend it to.
You could solve this by ensuring that the right type of quotes are around your date literal (it's actually a string literal that is interpreted as a date when compared to a date).
SELECT * FROM MyTable where DATE(Date) > '2021-11-02'
But it's far better to use query parameters, as mentioned in the comment above.
SELECT * FROM MyTable where DATE(Date) > #howFarBack
Then you don't need quotes. In fact you must not use quotes around the parameter placeholder.
See Parameterized Query for MySQL with C# or many other references for using parameters in SQL statements in C#.
Also remember that parameters can only be used in place of a single literal value. You can't use parameters for table or column identifiers, or a list of values, or SQL keywords, etc.
Related
I am trying to get a list of orders from my access database that were ordered between 2 dates.
this is my query:
"SELECT * FROM Orders WHERE OrderDate >= '#" + DateTime.Parse(txtDate.Text) + "#' AND OrderDate <= '#" + DateTime.Parse(txtEndDate.Text) + "#'";
the 2 textboxes recieve the dates directly from 2 ajax calendar extenders. i have already made sure that the first date is before the second, and i had made sure that the data inserted to the database was also inserted like '#"+date+"#', but i "still data type mismatch in query expression".
can anyone try and identify a problem with my query?
Assuming you have data like this:
var txtDate = new { Text = "2020-08-01" };
var txtEndDate = new { Text = "2020-08-01" };
your query might look like this:
var fromDate = DateTime.ParseExact(txtDate.Text, "yyyy-MM-dd",
CultureInfo.InvariantCulture).ToString("yyyMMdd");
var toDate = DateTime.ParseExact(txtEndDate.Text, "yyyy-MM-dd",
CultureInfo.InvariantCulture).ToString("yyyMMdd");
var query =
$"SELECT * FROM Orders WHERE OrderDate>='{fromDate}' AND OrderDate<='{toDate}'";
or the last part can be:
var query =
$"SELECT * FROM Orders WHERE OrderDate BETWEEN '{fromDate}' AND '{toDate}'";
It is the octothorpes that are delimiters for date expressions, not single quotes. And the formatted text expression for a date value should be forced. Thus:
"SELECT * FROM Orders WHERE OrderDate >= #" + DateTime.Parse(txtDate.Text).ToString("yyyy'/'MM'/'dd") + "# AND OrderDate <= #" + DateTime.Parse(txtEndDate.Text).ToString("yyyy'/'MM'/'dd") + "#";
That said, look up how to run queries with parameters. It takes a few more code lines but is much easier to debug and get right.
You should use a parameterized query for both security and for type safety
Access database queries use ? as a placeholder for the parameters you pass.
OleDbCommand command = new OleDbCommand();
// I assume you have an oledb connection object and it is in open state
command.Connection = myConnection;
command.CommandText = "SELECT * FROM Orders WHERE OrderDate >= ? AND OrderDate <= ?";
// Please note that the name of the parameters are redundant and not used
command.Parameters.AddWithValue("StartDate", DateTime.Parse(txtDate.Text));
command.Parameters.AddWithValue("EndDate", DateTime.Parse(txtEndDate.Text));
// You can also use your data adatper. This is just an example
command.ExecuteReader();
I'm running the following query
cmd = new SqlCommand("SELECT * FROM addresses WHERE identifier NOT IN(#notIn)", _connector.getMsConnection());
When I view the value notIn and copy this query I get an empty result on my database (which I'm expecting). However when I'm running this code I get 6 results. The content of string notIN is for example
string notIn = "'201619011124027899693E8M2S3WOCKT9G6KHE11' ,'201619011124027899693E8M2S3WOCKT9G6KHE12'"
which combined with
SELECT *
FROM addresses
WHERE identifier NOT IN(#notIn)
Should create
SELECT *
FROM addresses
WHERE identifier NOT IN ('201619011124027899693E8M2S3WOCKT9G6KHE11',
'201619011124027899693E8M2S3WOCKT9G6KHE12' )
which runs as expected.
it should be like this:
cmd = new SqlCommand(string.Format("SELECT * FROM addresses WHERE identifier NOT IN({0})", notIn), _connector.getMsConnection());
This way the value of notIn will be concat to your string query.
Contrary to what the other answers say, concatenating the string to build the SQL is a bad idea, especially since the input values are strings. You open yourself up to SQL injection attacks.
You should be generating multiple parameters for each item in your list.
For example, if you have the input input:
var notIn = new[] { "A1", "B2", "C3" }
You'd want something like
for(var i = 0; i < notIn.Length; i++)
command.AddParamWithValue("p"+i, notIn);
And then you can build the SQL with concatenation (note that we are not concatenating an input here)
var sql = "SELECT * FROM addresses WHERE identifier NOT IN(" + string.Join(",", notIn.Select(i,v) => { "#p" + i; }) + ")";
Which then would look like:
SELECT * FROM addresses WHERE identifier NOT IN (#p0,#p1,#p2)
Alternatively, you could dump the values into a temporary table and do a join.
Note that the above is pseudocode, and may not compile verbatim, but should give you the right idea about how to procede.
It's because, you passed the #notIn as a whole string, which means, the SQL server see it as:
SELECT * FROM addresses WHERE identifier NOT IN('''201619011124027899693E8M2S3WOCKT9G6KHE11'',''201619011124027899693E8M2S3WOCKT9G6KHE12''')
So you got empty result
Try changing the "not in" to where clause and generate the where with C#:
string selectStatement = "SELECT * FROM addresses WHERE";
selectStatement += " identifier != '201619011124027899693E8M2S3WOCKT9G6KHE11' and identifier != '201619011124027899693E8M2S3WOCKT9G6KHE12'";
Or if you really want to use parameterized SQL, try doing it in stored procedure instead.
Currently, I am creating an SQL Query by doing something like
string SQLQuery = "SELECT * FROM table WHERE ";
foreach(word in allTheseWords)
{
SQLQuery = SQLQuery + " column1 = '" + word + "' AND";
}
I understand that this can lead to an SQL Injection attack. I don't know how to pass an array as a parameter
where report in #allTheseWords
===========
I am using SQL Server 2012
Unfortunately, you cannot pass an array as a parameter without adding a user-defined type for table-valued parameters. The simplest way around this restriction is to create individually named parameters for each element of the array in a loop, and then bind the values to each of these elements:
string SQLQuery = "SELECT * FROM table WHERE column1 in (";
for(int i = 0 ; i != words.Count ; i++) {
if (i != 0) SQLQuery += ",";
SQLQuery += "#word"+i;
}
...
for(int i = 0 ; i != words.Count ; i++) {
command.Parameters.Add("#word"+i, DbType.String).Value = words[i];
}
You can also create a temporary table, insert individual words in it, and then do a query that inner-joins with the temp table of words.
Here is the recommendation from Microsoft:
Use Code Analysis to detect areas in your Visual Studio projects that are prone to sql injection;
Refer to the article on how to reduce risk of attack:
On short they talk about:
using a stored procedure.
using a parameterized command string.
validating the user input for both type and content before you build the command string.
Btw, you can enable static analysis as part of your build process and configure it so that when a security rule is broken, the build also breaks. Great way to make sure your team writes secure code!
Using ADO you can do it with the help of params
SqlConnection Con = new SqlConnection(conString);
SqlCommand Com = new SqlCommand();
string SQLQuery = "SELECT * FROM table WHERE ";
int i=1;
foreach(word in words)
{
Com.Parameters.Add("#word"+i.ToString(),SqlDbType.Text).Value = word;
SQLQuery = SQLQuery + " column1 = '#word"+i.ToString()+"' AND ";
i++;
}
Com.CommandText =SQLQuery;
For SQL Server, you'd use a Table-Valued Parameter. SQL has one structure that represents a collection of multiple items of the same type. It's called a table. It doesn't have arrays.
Of course, your supposed updated query:
where report in #allTheseWords
Isn't equivalent to your original query, but may be closer to the intent. In the query constructed using AND, you're saying that the same column, in the same row has to be equal to multiple different words. Unless all of the words are equal, this will never return any rows. The updated query answers whether any of the words match, rather than all.
You need to use prepared statements. The way those are handled is that you write your query and put placeholders for the values you want to use. Here's an example:
SELECT * FROM table WHERE column1 = #word
You then have to go through a prepare phase where the SQL engine knows it will need to bind parameters to the query. You can then execute the query. The SQL engine should know when and how to interpret the parameters you bind to your query.
Here's some code to do that:
SqlCommand command = new SqlCommand(null, rConn);
// Create and prepare an SQL statement.
command.CommandText = "SELECT * FROM table WHERE column1 = #word";
command.Parameters.Add ("#word", word);
command.Prepare();
command.ExecuteNonQuery();
I combine the use of params with HtmlEncoding(to get rid of special characters where not needed). Give that a shot.
using (SqlConnection conn = new SqlConnection(conString))
{
string sql = "SELECT * FROM table WHERE id = #id";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.paramaters.AddWithValue("#id", System.Net.WebUtility.HtmlEncode(id));
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
}
}
}
I am trying to log down sql that execute.
I have a function call LogGenerateReport(String Sql) that will do a insert process to save the data in a database.
The Problem i face is about the SQl ''.
For example:
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = 'ABC123';')
Its return me error and i know what happened because of the quote.
I try again inside my database where i open a new query and paste above sql and made some modification on it such as.
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = ''' + 'ABC123' + ''';')
Its return me expected result.
Output:
|Sql |
|SELECT * FROM Sales WHERE SalesID = 'ABC123';|
But back on my .aspx.cs page i have a string builder that store the executed query and before it executed, it need to save the query first.
For example:
System.Text.StringBuilder str = new System.Text.StringBuilder();
str.append("SELECT * FROM Sales WHERE SalesID = 'ABC123';");
api.LogGenerateReport(Convert.tostring(str));
Its return me error as like above because of the quote.
I try to figure it out to overcome this and my idea is
String TempSql = Convert.tostring(str);
TempSql.Replace("'","+'''");
I wont work because of the + symbol is at different position.
Is there any way to overcome this?
To succesfully log any and all SQL queries regardless of their content, you need to apply parameterized commands in the following way:
using(var command = new SqlCommand(someSqlConnection))
{
command.CommandText = "INSERT INTO TABLE(Sql) VALUE(#Sql)";
command.Parameters.AddWithValue("#Sql", "<any string>");
command.ExecuteNonQuery();
}
That way you can avoid escaping anything with NON-STANDARD methods, and protect your code from SQL injection attacks.
cmd.CommandType = CommandType.Text;
cmd.CommandText = #"SELECT * FROM Sales WHERE SalesID = #Param";
cmd.Parameters.Add("#Param", SqlDbType.Varchar).Value = 'ABC';
Try this out and using paramterised one is highly recommended and to some extent handling sql injection as well as these sorts of problems
Why can't you just replace all single quotes within the statement with double single qoutes?
This request works fine:
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = ''ABC123'';')
And the code:
String TempSql = Convert.tostring(str);
TempSql.Replace("'","''");
My guess is your api.LogGenerateReport isn't escaping single quotes when inserting the row into the database. You should fix your insert statement using paramaters.
To get it to "work" as is, you need to escape your single quotes with two single quotes. Try changing:
"SELECT * FROM Sales WHERE SalesID = 'ABC123';"
to
"SELECT * FROM Sales WHERE SalesID = ''ABC123'';" <-- these are 2 single quotes, no double quotes
Good luck.
I have a MySQL database, there's a table which have column Time's Type is Nvachar(50) and its values is kind like this "05/09/2012 20:53:40:843" *(Month-date-year hour:mins:second:msecond)*
Now I want to query to get a record have Time after "10/05/2012 01:00:30 PM".
I had code in C# to converted it to "05/10/2012 13:00:30" before making a query.
My Query :
SELECT * FROM ABCDFEGH WHERE capTime > '05/10/2012 13:00:30' LIMIT 0, 1
But i got no record. So please tell me how can I can make it return record have time after the time above ???
More Info My C# code :
string tableName = "ABCDFEGH";
string date = "05/10/2012 13:00:30";
var query = "SELECT * FROM " + tableName + " WHERE capTime > '" + date + "' LIMIT 0, 1";
var cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = null;
try
{
dataReader = cmd.ExecuteReader();
}
I'm so so so so so so sorry. I made a mistake the query must be
SELECT * FROM ABCDFEGH WHERE capTime > '05/10/2012 13:00:30' LIMIT 0, 1
This query is successful return the record i need :)
But soemhow I have mistyped it into
SELECT * FROM ABCDFEGH WHERE capTime > '05-10-2012 13:00:30' LIMIT 0, 1
Sorry again, topic close. But tks for evveryone tried :)
I recommend using the DATETIME datatype instead of NVARCHAR. Store dates in YYYY-MM-DD HH:MM:SS format, which is the native DATETIME format recognized by MySQL.
Also use date literals in the same format.
Two reasons for this recommendation: First, DATETIME takes only 8 bytes, instead of up to 150 bytes which is the potential size of a multibyte 50 character varchar.
Second, the sort order of DATETIME will be the same as the chronological order. So if you create an index on the Time column, your > comparison can benefit from the index. Your query will be much faster as a result.
Use TIMESTAMPDIFF()
Schema
CREATE TABLE ABCDFEGH (`right` varchar(3), `time` datetime);
INSERT INTO ABCDFEGH (`right`, `time`)
VALUES
('Yes', '2012-10-02 13:00:30'),
('No', '2012-10-15 13:00:30');
SQL Code
SELECT * FROM ABCDFEGH
WHERE TIMESTAMPDIFF(MINUTE, time, '2012-10-05 13:00:30') > 0
LIMIT 0, 1
Explanation
TIMESTAMPDIFF() returns datetime_expr2 – datetime_expr1, where datetime_expr1 and datetime_expr2 are date or datetime expressions. One expression may be a date and the other a datetime; a date value is treated as a datetime having the time part '00:00:00' where necessary. The unit for the result (an integer) is given by the unit argument.
Fiddle: http://www.sqlfiddle.com/#!2/244cc/1 datetime
Fiddle: http://www.sqlfiddle.com/#!2/063b3/1 varchar(50)
PS1: Time may be a reserved word. Please avoid using it. Else use it with backticks (`).
PS2: The format of time is YYYY-MM-DD not the reverse.
First, why did you save the dates as NVARCHAR? If you are still able to change it to DATETIME datatype and all of the records on it, much better.
But if not, you can use STR_TO_DATE.
SELECT *
FROM tableName
WHERE STR_TO_DATE(`capTime`, '%m/%d/%Y %H:%i:%s:%x') >
STR_TO_DATE('05/10/2012 13:00:30', '%c/%d/%Y %H:%i:%s')
See SQLFiddle Demo
SOURCES
STR_TO_DATE
DATE Formats
UPDATE 1
and your query is vulnerable with SQL Injection. To avoid from it
Parameterized your query
code snippet,
string tableName = "ABCDFEGH";
string date = "05/10/2012 13:00:30";
String query = "SELECT * FROM " + tableName + " WHERE STR_TO_DATE(`capTime`, '%m/%d/%Y %H:%i:%s:%x') > STR_TO_DATE(#dateHere, '%c/%d/%Y %H:%i:%s')";
using (MySqlConnection connection = new MySqlConnection("connectionStringHere"))
{
using (MySqlCommand command = new MySqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandText = query;
command.Parameters.AddwithValue("#dateHere",date)
MySqlDataReader dataReader = null;
try
{
dataReader = cmd.ExecuteReader();
}
catch(MySqlException e)
{
// do something here
// don't suppress the error
}
}
}