where statement doesn't work correctly in c# - c#

I am using MYSQL in my c# app.
There are some string values in mysql table and there is a textbox that client can write in that and then I use this statement to show the results:
"SELECT ID,METER FROM DB.TABLE1 WHERE METER >= '" +TEXT1.text.tostring+"'";
But for Example, if the client write 400 in that textbox, the results are like this:
50,400,500,600,50,500
But we know that 50 is not bigger than 400!!!
And then I used this code:
"SELECT ID,METER FROM DB.TABLE1 WHERE METER <= '" +TEXT1.text.tostring+"'";
If the client write 400 in that textbox, the results are like this:
300,150,100,250;
50 is not shown!!!
Can you please help me what should I do???

50 is "bigger" than 400 when you treat them as strings, so I suspect that's what you're doing.
First: never, never, never build SQL like this. It's susceptible to SQL injection attacks, it leads to error-prone conversions, and it makes your code harder to read.
Instead, use parameterized SQL. In this case you'll want SQL of something like:
SELECT ID,METER FROM DB.TABLE1 WHERE METER >= #Parameter
and then set the #Parameter parameter in the parameter collection - parsing it as an integer (or whatever type is suitable for the values you're trying to represent) first.
Next: check the type of METER in your schema. If it's varchar or some similar text type, you need to fix that. It should be a suitable numeric type if you want to treat the values as numbers.

< and > operator will work perfeclty on numeric values as you have added single quotes to the values you have passed, it may be causing the issue. try this
"SELECT ID,METER FROM DB.TABLE1 WHERE METER >= "+TEXT1.text.tostring;

Convert user input to Integer and then use the integer value in your query.
SELECT ID,METER FROM DB.TABLE1 WHERE METER >= Convert.ToInteger(TEXT1.text.ToString()) ;

Text comparison isn't numeric comparison. To avoid this misleading behavior and sql injection risk you must use parametrized queries.

You can cast a string to a number in MySQL:
var sql = #"
SELECT ID, METER
FROM DB.TABLE1
WHERE CAST(METER AS UNSIGNED) >= "
+ Convert.ToInteger(TEXT1.text.ToString());
There are all kinds of oddities to consider when dealing with numbers as strings in MySQL.
I suggest reading: https://dev.mysql.com/doc/refman/5.0/en/type-conversion.html
As Jon has suggested, a better solution would be to ALTER the table schema to use a numeric column datatype and use SQL parameters in your code, especially as you seem to be dealing with user input.

Related

Creating a key-value pair dynamically and then... and then

Is it possible to create a key-value pair from an HTML radio button and text box value, then insert that pair into an array, then pass that array into a SQL Stored Procedure parameter that adds to the WHERE clause in the SP?
I am building a dashboard app that references one table in the SQL Server DB when it starts. This brings back the results of a simple select SP. I am pretty new to SQL so I am "psuedo-coding"(sorry)
DECLARE #SelectedDate NVARCHAR(50)
SELECT * FROM Tablename WHERE log_ts > #SelectedDate
However at this point I need to be able to select between 1-4 field names and give them a value to filter by.
I would love to set up those field names as a radio button, and to prevent injection, screen those inputs against known values in my application before sending them to the server, including inserting default values of the input is null.
My thought was to set up an array[string, string] once the screening is done, and pass that array into SQL as an array[field, value].(there would never be a null value).Here is what i would like the array to look like when being sent and all pairs are their default values:
QueryArray = ["application_name","Mobile"]
["login_id","*"]
["log_ts","Datetime.Today"]
["error_level","Exception"]
Then somehow pass it into the where clause like this:
SELECT *
FROM TableName
WHERE [application_name] = [Mobile]
AND [login_id]=[*]
AND [log_ts]=[Datetime.Today]
AND [error_level]=[Exception]
This is where my brain looks normally for a for-each or something, but SQL if pretty new to me... Any help would be greatly appreciated, even if its just a link to a previous article that I could not find...
It sounds like you want to use nullable parameters in conjunction with the coalesce operator. This works well if you have a known set of parameters, and then want to filter against them. So if you know all the possible fields you can search on, which is assuming you know ahead what fields you will display on your UI for people to filter with, then you can actually make a procedure like so:
create procedure [dbo].[MySampleProcedure]
#Property1Value <yourDataType> = null,
#Property2Value <yourDataType> = null,
....
as
begin
select
Col1,
Col2,
...
from
[dbo].[YourTable]
where
Column1Value = coalesce(#Property1Value, Column1Value)
and Column2Value = coalesce(#Property2Value, Column2Value)
....
end
This effectively means that if you omit certain parameters, you can still match based on the parameters you provide. When you receive your set from the UI, you can then match the pairs to parameters and you're off to the races.
If your field names are dynamic (not known ahead of time), then it becomes a more difficult issue. In that case, you will likely have to drop to dynamic SQL to accomplish what you want, and construct a query using parameterized SQL, to avoid injection attempts.

Access SQL Delete DateTime not working

I am trying to execute following code for I have hardcoded the date in but it doesn't seem to delete it before I had data mismatch expection.
string myQuery = "DELETE FROM Class WHERE Date=#10/12/2015#;";
I ran the SQL query in Access and it didn't work. So I generated it using the tools avaliable there and it looks like that:
DELETE Class.ClassDate FROM Class WHERE (((Class.ClassDate)=#12/10/2015#));
Instead of:
string myQuery = "DELETE FROM Class WHERE Date=#10/12/2015#;";
use
string myQuery = "DELETE FROM Class WHERE Date=#10/12/2015#";
Be sure not to put ; before " in Access SQL in C#
A good reality check is to replace DELETE with SELECT *, if the WHERE clause doesn't return any rows in a SELECT query, it isn't going to delete any either.
Is it possible there is a time component stored in the field Date? The value #10/12/2015# is implicitly #10/12/2015 12:00 AM#, and a test for equality will only return rows that match exactly. If you're actually looking for all rows with any time of day on that date...
WHERE [Date] BETWEEN #10/12/2015# AND #10/12/2015 11:59 PM#
(Not sure if Date is a reserved word in Access -- life is too short to depend on Jet -- but even if it isn't the square brackets won't hurt anything.)

Can a ADO.NET SQL command parameter contain a sub-query?

Is it possible to create a SQL select max(id) as a variable inside a query?
This doesn't work:
command.CommandText = "INSERT INTO ordreid (ordrenr,ordreid) SELECT #ordrenr, #ordreid";
command.Parameters.AddWithValue("#ordrenr", nyordre);
command.Parameters.AddWithValue("#ordreid", ("MAX(ordreid)+1 FROM ordreid"));
command.ExecuteScalar();
Here is a photo of what I'd like to do. 5 customers have added items to their orders. Customer 1 has 3 items, customer 2 has 4 items, customer 3 has 1 item, and so on...
This way I have 1 ordreid even through the order could consist of 30 items.
Query parameters cannot include SQL verbatim.
This is how such prepared statements prevent SQL Injection as they are not directly inserted. Rather only the corresponding data is used in the query - in this case that is the string that contains SQL, and results in invalid SQL syntax.
The SQL text then needs to look similar to the following, although this probably does not do what is desired. (Asking how to do the higher level task will lead to actually useful queries/approaches.)
#"INSERT INTO ordreid (ordrenr, ordreid)
SELECT #ordrenr, MAX(ordreid)+1
FROM ordreid"
command.Parameters.AddWithValue("#ordreid", ("MAX(ordreid)+1 FROM ordreid"));
The method name AddWithValue hints at the reason why this won't work: Parameters contain data, that is: values. They cannot represent a part of a SQL statement.
Your intention appears to be that #ordreid should be replaced with that piece of SQL. If this is so, then there's no reason to even have a parameter. Simply perform the substitution manually and change the CommandText:
command.CommandText = #"INSERT INTO ordreid (ordrenr, ordreid)
VALUES (#ordrenr, (SELECT MAX(ordreid)+1 FROM ordreid));";
Note that I changed four things (apart from spreading the command text across two lines, for legibility's sake, using C#'s #"…" string syntax). Only the first two points are crucial:
I moved the MAX(…) SQL directly into your CommandText. This makes the (invalid) #ordreid parameter obsolete.
To determine the value of MAX(ordreid) FROM ordreid, you need a sub-query; thus the added SELECT before MAX. Otherwise, the syntax wouldn't be valid.
I replaced your SELECT with a VALUES table value constructor. (Otherwise, because of the previous point, we'd have two SELECTs very close to each other, which would look somewhat confusing.)
I added a ; at the end of your query. Current versions of SQL Server don't yet require such a statement terminator, but Microsoft has hinted that they might become compulsory in future versions of T-SQL. I therefore believe that it's a good habit to get into now.
That all being said, you should probably turn the ordreid column into an IDENTITY column and let SQL Server choose the value to be inserted (making the SELECT MAX(…) business obsolete). Otherwise, if two processes or threads execute the same INSERT command at the same time, you might end up with several rows having the same value for ordreid.

Sql ORDER BY alphabetically no casesensitive

Today I encountered problem that causes difficulty for me to solve it.
In application I want to display records in aphabetical order thus in SQL statement I'am using ORDER BY, But it looks like CAPITAL letters are before lowercase letters so record starting with Z is before a.
This is example of my sql statement
SELECT * FROM myTable WHERE id= 5 ORDER BY name
Do you have any ideas ? Can I sort data in DataTable object after retreiving it from database ? or can it be accomplished by more complex sql statement?
Any ideas will be appreciated
You can modify your SQL query in such a way that all capitals are transformed to lower before ordering
SELECT * FROM myTable WHERE id = 5 ORDER BY LOWER(name)
The rules for comparing text values is the collation; there are many many collations available in SQL Server, and most have both case-sensitive and case-insensitive options.
If you don't want to change the collation (in particular, if this applies only to specific cases), you can also use functions like LOWER / UPPER, but this cannot make efficient use of indexes. A hybrid approach is to store redundant information: store the original data in one column, and the standardized data (perhaps all lower-case-invariant) in a second column. Then you can index the two separately (as you need), and operate on either the original or standardized data. You would normally only display the original data, though. Persisted+calculated+indexed columns might work well here, as then it is impossible to get inconsistent data (the server is in charge of the calculated column).
Try
SELECT * FROM myTable WHERE id= 5 ORDER BY LOWER(name)
OR
SELECT * FROM myTable WHERE id= 5 ORDER BY LCASE(name)
depending on which database you are using
You can perform ordering by providing case in SQL. Just do this:
SELECT * FROM myTable WHERE id= 5 ORDER BY UPPER(name)
OR
SELECT * FROM myTable WHERE id= 5 ORDER BY UCASE(name)
Ordering will be done on upper case name while you result will be same as present in table.
Try this...
SELECT * FROM myTable WHERE id= 5 ORDER BY name COLLATE Latin1_General_100_CI_AS

LINQ to SQL Decimal Parameter

I have a very simple linq to sql query in C#:
int acctNum = 12345;
var query = from p in db.table
where p.ACCT_NO == acctNum
select p;
This generates the following SQL:
exec sp_executesql N'SELECT [t0].field1, [t0].field2, [t0].ACCT_NO
FROM [dbo].[table] AS [t0]
WHERE [t0].[ACCT_NO] = #p0', N'#p0 decimal(29,0)', #p0 = 12345
For some reason, this is taking an incredibly long time to run (several minutes). If I run an equivalent query in management studio (select * from table where acct_no = 12345), it takes less than a second on a very large table (~7MM rows). After some digging with the SQL profiler, I found that linq is passing the acctNum parameter as a Decimal(29,0) while the field is stored in the database as a Numeric(18,0). If I take the generated SQL and just change the parameter type from decimal to numeric, it runs in less than a second. In the profiler, I can see that the linq version uses almost 2 million reads versus about 400 for the numeric parameter query. How can I force linq to pass this parameter as numeric instead of decimal?
Most likely the problem lies with the type of p.ACCT_NO (in other words it was probably generated as a floating-point numeric type). Make sure that this property is also typed as an int and it should work.

Categories