SQL Command c# not getting the right results - c#

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.

Related

MySQL filter by date

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.

Sql/Dapper: How do perform LIKE in WHERE clause for array of values?

I have a table with a lot of employees in it, every person has a Name column with their full name.
I then want to do a query similar to this when searching for people:
SELECT * FROM Employees WHERE Name LIKE '%' + #value1 + '%' AND Name LIKE '%' + #value2 +'%' AND so forth...
for an arbitrary array of values.
My Dapper code would look something like this:
public IEnumerable<Employee> Search(string[] words)
{
using var connection = CreateConnection();
connection.Query<Employee>("SELECT * etc.", words);
}
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
Caveat: I don't know how Dapper actually passes an array to the query, which limits my creative ideas for working around this :-D
And also: Changing the Table structure is, unfortunately, out of the question. And I'd rather avoid fetching every single person into .Net memory and doing the filtering there.
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
Because the set of where conditions is not fixed you will need to build the query dynamically. But that does not mean you cannot parameterise the query, you just build the parameter list alongside building the query. Each time a word from the list add to the condition and add a parameter.
As Dapper doesn't directly include anything that takes a collection of DbParameter, consider using ADO.NET to get an IDataReader and then Dappter's
IEnumerable<T> Parse<T>(this IDataReader reader)
for the mapping.
Such a builder would be very roughly
var n = 0;
for (criterion in cirteria) {
var cond = $"{crition.column} like #p{n}";
var p = new SqlPatameter($"#p{n}", $"%{crition.value}%";
conditions.Add(cond);
cmd.Parameters.Add(p);
}
var sql = "select whetever from table where " + String.Join(" and ", conditions);
cmd.CommandText = sql;
var reader = await cmd.ExecuteReaderAsync();
var res = reader.Parse<TResult>();
For performance reasons, it's much better to do this as a set-based operation.
You can pass through a datatable as a Table-Value Parameter, then join on that with LIKE as the condition. In this case you want all values to match, so you need a little bit of relational division.
First create your table type:
CREATE TYPE dbo.StringList AS TABLE (str varchar(100) NOT NULL);
Your SQL is as follows:
SELECT *
FROM Employees e
WHERE NOT EXISTS (SELECT 1
FROM #words w
WHERE e.Name NOT LIKE '%' + w.str + '%' ESCAPE '/' -- if you want to escape wildcards you need to add ESCAPE
);
Then you pass through the list as follows:
public IEnumerable<Employee> Search(string[] words)
{
var table = new DataTable{ Columns = {
{"str", typeof(string)},
} };
foreach (var word in words)
table.Rows.Add(SqlLikeEscape(word)); // make a function that escapes wildcards
using var connection = CreateConnection();
return connection.Query<Employee>(yourQueryHere, new
{
words = table.AsTableValuedParameter("dbo.StringList"),
});
}

Selecting all rows that match parts of a string

I have a table of people
Id | Name | Info
1 | Bob | some info
2 | Mark | some info
And I have a list of names in a string separated by commas which looks like:
"Mark, Bob, John"
I need an SQL command that would select all rows that match the names in the list.
Any idea how to do it?
It's in c# on wpf and the database is PostgreSQL if that matters.
In general case, you can try building parametrized query:
string names = "Mark, Bob, John";
string[] filters = names
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(name => name.Trim())
.Where(name => !string.IsNullOrEmpty(name))
.ToArray();
//TODO: Put the right class instead of SqlConnection
using (var connection = new SqlConnection("ConnectionStringHere")) {
connection.Open();
using (var command = connection.CreateCommand()) {
command.Connection = connection;
command.CommandText =
$#"select *
from MyTable
where Name in ({string.Join(", ", filters.Select((name, i) => $"#prm_Name{i}"))})";
//TODO: Change AddWithValue into Add and provide the RDBMS type
for (int i = 0; i < filters.Length; ++i)
command.Parameters.AddWithValue($"#prm_Name{i}", filters[i]);
using (var reader = command.ExecuteReader()) {
...
}
}
}
create an ad-hoc query from your name string like:
string Names = "Mark, Bob, John";
//Step 1: add quotes to Names
var names = Names.Split(',').Select(x => $"'{x}'").ToList();
//Step 2: Join Quoted Names
var result = String.Join(",", names.ToArray());
//Step 3 Create Ad hoc query
string query = $"SELECT * FROM people WHERE NAME IN ({result})";
I need an SQL command that would select all rows that match the names in the list.
How about IN?
SELECT * FROM People WHERE Name IN ('Mark', 'Bob', 'John')
You should take the string apart with C# first. There are many different ways to do the loop, but after you substring the names, place them in an array or a list or something. Within the loop, look for the names using the LIKE clause.
'SELECT *
FROM myTable
WHERE LOWER(nameField) LIKE LOWER(%' + variable + '%)';
Not sure how you're building your SQL but if you are using a string it would look kind of like this. You can take out the LIKE if you're only looking for direct matches. Its better to place all values in either lowercase or uppercase to make sure you're getting them UNLESS you know FOR SURE you want Mark not mark or MARK etc. I do not know C#, so I am sure there's a much better way, with adding parts of an array together with commas and such to use the IN operator.

dynamic sql database name replace

I have a requirement where I have to replace the database names in the dynamic sql with the new database names .
something like following:
select * from DBName.TableName
should become
select * from newDBName.TableName
select * from [DbName].TableName
should become
select * from [NewDbName].TableName
I have tried string.Replace() method but that doesn't respect all combinations a programmer can write the database names in a dynamic sql
Please help.
Instead of trying to replace parts of a string, you should use a formatted string and pass in the parameter. For example:
var query = String.Format("select * from {0}.TableName", newDB)
used in a method where you can pass in the new db name and return the new query.
public static string GetNewQuery(string newDB) {
return String.Format("select * from [{0}].TableName", newDB);
}
would result in "select * from [newDB].TableName"
https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx

How to build a parameterised query with IN sql keyword?

That's what I tried & failed:
string sql = "... WHERE [personID] IN (#sqlIn) ...";
string sqlIn = "1,2,3,4,5";
SqlCeCommand cmd.Parameters.Add("#sqlIn", SqlDbType.NText).Value = sqlIn;
SqlCeDataAdapter da = new SqlCeDataAdapter(cmd);
da.Fill(ds); // > Error
Error details:
The ntext and image data types cannot be used in WHERE, HAVING, GROUP BY, ON, or IN clauses, except when these data types are used with the LIKE or IS NULL predicates.
Can't I pass all the IDs as one parameter? Should I add one by one all IDs?
P.S: Notice SqlCE
You can't parameterise that as a single parameter. Your query is doing an "in" on a single value, so is essentially:
... Where personId = '1,2,3,4,5'
(give or take a parameter). This is usually also an invalid or sub-optimal equality test, and certainly isn't what you were trying to query.
Options;
use raw concatenation: often involves a SQL injection risk, and allows poor query plan re-use
on full SQL server: use a UDF to split a single param
on full SQL server, use a TVP
add parameters dynamically, and add the various #param3 etc to the TSQL
The last is the most reliable, and "dapper-dot-net" has a feature built in to do this for you (since it is commonly needed):
int[] ids = ...
var rows = conn.Query<SomeType>(
#"... Where Id in #ids",
new { ids }).ToList();
This, when run via dapper-dot-net, will add a parameter per item in "ids", giving it the right value etc, and fixing the SQL so it executes, for example:
"... Where Id in (#ids0, #ids1, #ids2)"
(if there were 3 items in "ids")
You'll need to split the sqlIn string by comma, convert each to an integer, and build the IN statement manually.
string sqlIn = "1,2,3,4,5";
string inParams = sqlIn.Split(',');
List<string> paramNames = new List<string>();
for(var i = 0; i < inParams.Length; ++i){
string paramName = "#param" + i.ToString();
SqlCeCommand cmd.Parameters.Add(paramName, SqlDbType.Int).Value = int.Parse(inParams[i]);
paramNames.Add(paramName);
}
string sql = "... WHERE [personID] IN (" +
string.Join(",", paramNames) +
") ...";

Categories