Can someone tell me why this SQL query not working - c#

I followed this answer,
How can I supply a List<int> to a SQL parameter?
Please see these questions of mine for understanding scenario,
How can I update Crate IDs of List of Fruits in single SQL query in c#
how can i update SQL table logic
What I am trying and not working
private void relate_fruit_crate(List<string> selectedFruitIDs, int selectedCrateID)
{
string updateStatement = "UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID = #selectedFruitIDs";
using (SqlConnection connection = new SqlConnection(ConnectionString()))
using (SqlCommand cmd = new SqlCommand(updateStatement, connection))
{
connection.Open();
cmd.Parameters.Add(new SqlParameter("#selectedCrateID", selectedCrateID.ToString()));
cmd.Parameters.Add(new SqlParameter("#selectedFruitIDs", String.Join(",",selectedFruitIDs.ToArray())));
cmd.ExecuteNonQuery();
}
}
My code runs without any error,

You need to use the IN keyword in your scenario. The problem is that the SqlCommand.Parameters pattern does not build the query itself, but calls a stored procedure on the database:
exec sp_executesql N'UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID in(''#selectedFruitIDs'')', N'#selectedCrateID nvarchar(1),#selectedFruitIDs nvarchar(5)', #selectedCrateID = N'1', #selectedFruitIDs = N'1,2'
This will not work as the array is escaped.
The workaround would be to either use a normal StringBuilder to create the query. (Warning! SQL Injection) or to call the query for each ID separately.
Maybe there's a way to do this with the SqlCommand.Parameters, but I could not find one.
OLD POST::
string updateStatement = "UPDATE relate_fruit_crate set CrateID IN ('#selectedCrateID') where FruitID = '#selectedFruitIDs'";
[....]
cmd.Parameters.Add(new SqlParameter("#selectedFruitIDs", String.Join("','",selectedFruitIDs.ToArray())));
and equals (=) query will only match a single value.

Multi-value parameter queries are a bit of a pain in TSQL. There are options like table-valued parameters, or "split" UDFs - otherwise... it is a bit tricky. You end up having to add multiple parameters (depending on the data), and change the query to suit. If I may suggest... a library like "dapper" may help you here - it is designed to make scenarios like this easy:
using Dapper; // at the top of your code file, to enable dapper
...
private void relate_fruit_crate(List<string> selectedFruitIDs, int selectedCrateID)
{
// note the slightly unusual "in" here (no paranethesis) - that is because
// dapper is going to do some voodoo...
using (SqlConnection connection = new SqlConnection(ConnectionString()))
{
connection.Open();
connection.Execute(
"UPDATE relate_fruit_crate set CrateID = #selectedCrateID where FruitID in #selectedFruitIDs",
new { selectedFruitIDs, selectedCrateID });
}
}
here "dapper" does all the work of figuring out how to express that in using multiple parameters, adding the correct number of parameters. It is also just much easier (in particular, look at how little work we did with commands and parameters; it handles readers nicely too).
Dapper is freely available from NuGet

Related

Get output 'inserted' on update with Entity Framework

SQL Server provides output for inserted and updated record with the 'inserted' keyword.
I have a table representing a processing queue. I use the following query to lock a record and get the ID of the locked record:
UPDATE TOP (1) GlobalTrans
SET LockDateTime = GETUTCDATE()
OUTPUT inserted.ID
WHERE LockDateTime IS NULL
This will output a column named ID with all the updated record IDs (a single ID in my case). How can I translate this into EF in C# to execute the update and get the ID back?
Entity Framework has no way of doing that.
You could do it the ORM way, by selecting all the records, setting their LockDateTime and writing them back. That probably is not safe for what you want to do because by default it's not one single transaction.
You can span your own transactions and use RepeatableRead as isolation level. That should work. Depending on what your database does in the background, it might be overkill though.
You could write the SQL by hand. That defeats the purpose of entity framework, but it should be just as safe as it was before as far as the locking mechanism is concerned.
You could also put it into a stored procedure and call that. It's a little bit better than the above version because at least somebody will compile it and check that the table and column names are correct.
Simple Example #1 to get a data table:
I did this directly against the connection:
Changed the command.ExecuteNonQuery() to command.ExecuteReader()
var connection = DbContext().Database.Connection as SqlConnection;
using (var command = connection.CreateCommand())
{
command.CommandText = sql;
command.CommandTimeout = 120;
command.Parameters.Add(param);
using (var reader = command.ExecuteReader())
{
var resultTable = new DataTable();
resultTable.Load(reader);
return resultTable;
}
}
FYI, If you don't have an OUTPUT clause in your SQL, it will return an empty data table.
Example #2 to return entities:
This is a bit more complicated but does work.
using a SQL statement with a OUTPUT inserted.*
var className = typeof(T).Name;
var container = ObjContext().MetadataWorkspace.GetEntityContainer(UnitOfWork.ObjContext().DefaultContainerName, DataSpace.CSpace);
var setName = (from meta in container.BaseEntitySets where meta.ElementType.Name == className select meta.Name).First();
var results = ObjContext().ExecuteStoreQuery<T>(sql, setName, trackingEnabled ? MergeOption.AppendOnly : MergeOption.NoTracking).ToList();
T being the entity being worked on

select query does not work with parameters using Parameters.AddWithValue

The following query in C# doesn't work, but I can't see the problem:
string Getquery = "select * from user_tbl where emp_id=#emp_id and birthdate=#birthdate";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
Reader.HasRows returns no result but when I query it to my database I got data.
I'll assume your code is actually not quite as presented, given that it wouldn't currently compile - you're using cmdR before you declare it.
First, you're trying to use named parameters, and according to the documentation of OdbcCommand.Parameters, that isn't supported:
When CommandType is set to Text, the .NET Framework Data Provider for ODBC does not support passing named parameters to an SQL statement or to a stored procedure called by an OdbcCommand. In either of these cases, use the question mark (?) placeholder.
Additionally, I would personally avoid using AddWithValue anyway - I would use something like:
string sql = "select * from user_tbl where emp_id = ? and birthdate = ?";
using (var connection = new OdbcConnection(...))
{
connection.Open();
using (var command = new OdbcCommand(sql, connection))
{
command.Parameters.Add("#emp_id", OdbcType.Int).Value = userValidate.EmployeeId;
command.Parameters.Add("#birthdate", OdbcType.Date).Value = userValidate.BirthDate;
using (var reader = command.ExecuteReader())
{
// Use the reader here
}
}
}
This example uses names following .NET naming conventions, and demonstrates properly disposing of resources... as well as fixing the parameter issue.
I do think it's slightly unfortunate that you have to provide a name for the parameter when adding it to the command even though you can't use it in the query, but such is life.
Use like this:
string Getquery = "select * from user_tbl where emp_id=? and birthdate=?";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
while(Reader.Read())
{
//Do something;
}
I know this thread is old, but I wanted to share my solution for anyone else coming up on this.
I was having issues with the typical method that Jon posted. I have used it before, but for some reason with this new string I had it was not wanting to actually place the parameter correctly and was causing the reader to not work.
I ended up doing something like this instead, since in the end we are just replacing parts of a string.
string sql = "select * from user_tbl where emp_id = "+ var1 +" and birthdate = "+
var2""
OdbcCommand command = new OdbcCommand(sql);
This was easier for me to get to work. Be warned though, I am not sure if it has any specific drawbacks when compare to using the command parameter method.

C# Using sql parameters which now won't work

Hey I was using parametrized queries for my application which worked just fine but now (I don't know why) they aren't replaced anymore with the values...
So instead of running something like "SELECT [TABLE_NAME] FROM [MyDefinetelyExistingDatabase]"; it tries to execute "SELECT [TABLE_NAME] FROM [#targetDatabase]"; which, of course, will fail.
var dataBaseToGetTablesFrom = "MyDefinetelyExistingDatabase";
var results = new List<string>();
const string query = #"SELECT
[TABLE_NAME] AS tableName
FROM
[#targetDatabase].[INFORMATION_SCHEMA].[TABLES] ;";
using (var context = new ConnectionHandler(true))
{
if (context.Connection.State != ConnectionState.Open)
throw new ConnectionFailedException(context.Connection.State);
using (var command = new SqlCommand(query, context.Connection))
{
command.Parameters.AddWithValue("#targetDatabase", dataBaseToGetTablesFrom);
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
return results.ToArray();
while (reader.Read())
results.Add(reader.GetString(0));
}
}
}
return results.ToArray();
I now tried different formats and things to add the parameters but it results in the same...
I don't want to do this by inserting the values into the query directly via string.Format eg but I want to have those parameters (which work properly at different places in the code (???) but not where I want.
In fact, I need to use parameters in every statement and must be able to address different databases by calling them like [DB].[Table-Schema].[Table]
[EDIT]
Hey guys, figured the problem some days ago and thought I share it with you.
As far as I have noticed, my problem at the whole was to try to replace the databasename and / or in some other examples, the table name as well.
So this won't work which makes clearly sense to me as the server can't prepare to execute a statement if it doesn't even know on which table it should work and therefore doesn't know anything about the structure etc.
So I changed my statements to fit my new knowledge and it worked as expected like a charm.
I don't know what ConnectionHandler is, but if that is your own code you can implement it with SqlConnectionStringBuilder which will allow you to use a variable to assign the InitialCatalog instead of putting the database name in the query. This would be preferable to dynamic sql which requires careful sanitization.
You would need dynamic sql for this something like.....
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' SELECT [TABLE_NAME] AS tableName '
+ N' FROM ' + QUOTENAME(#targetDatabase) + N'.[INFORMATION_SCHEMA].[TABLES]'
Exec sp_executesql #Sql

SQL Syntax in C#

I'm trying to understand why in C# if you have a sql string why you would have to put tick (') marks in the following where clause in order for this to work. Could someone please explain the reasoning behind this?
where ProgramServer='" + machineName.ToString() + "' and Active=1;
You can avoid those tick (') marks and use Parameters, They will also save you from SQL Injection.
The reason you see those ticks are because SQL expects string type values to be enclosed in single ticks.
What you're seeing is a dynamically built SQL query in the code. When querying based on a string value, the string must be wrapped in single quotes. The final SQL string would look something like:
select * from someTable where ProgramServer = 'YourMachineName' and Active = 1;
Unfortunately, that is far from the best way to do things. You should be using parameterized queries instead:
var query = "select * from someTable where ProgramServer = #machineName and Active = 1;";
using(var conn = new SqlConnection(connString))
{
var command = new SqlCommand(query, conn);
command.Parameters.Add("machineName", machineName.ToString());
// Execute and get the results
}

Why are parameters slower than literal values in a where clause?

Situation: c#, sql 2000
I have a table, lets call it 'mytable' with 30 million rows.
The primary key is made up of fields A and B:
A char(16)
B smallint(2)
When i do a search like this, it runs really slowly (eg it does a full tablescan)
string a="a";
int b=1;
string sql = "select * from table(nolock) where a=#a and b=#b";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#a", a);
cmd.Parameters.AddWithValue("#b", b);
using (SqlDataReader rdr = cmd.ExecuteReader()) {...}
}
Change it to this however, and it runs really quick (eg it hits the index):
string where =
String.Format("a='{0}' and b={1}", a, b);
string sql = "select * from table(nolock) where " + where;
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataReader rdr = cmd.ExecuteReader()) {...}
}
What on earth is going on? Seems strange to me.
Do data types of parameter and column match? They don't it appears so datatype precedence applies
The column is smallint, but you send int. The column will be converted to int because it has a higher precedence. So it won't use an index.
Does it make any difference if you declare the b variable to be a short instead of int?
Does it make any difference if you explicitly specify the types of the parameters?
Does it make any difference if you use "where a=#a and b=#b" instead of the comma form?
I agree this does sound odd, and I wouldn't really expect any of these changes to help, but it's probably worth a try.
You may tell SQL Server which index to use for a query. Use the WITH (INDEX = INDEX_ID) option where INDEX_ID is the ID of the index.
Get index ID's with:
SELECT i.indid, i.name FROM sysindexes i
INNER JOIN sysobjects o ON o.ID = i.id
WHERE o.Name = 'table'
So try then:
SELECT * FROM table(NOLOCK) WITH (INDEX = 1) WHERE a=#a and b=#b
As #gbn said, setting the data type should make it easy for you.
string where =
String.Format("a='{0}' and b={1}", a, b);
In the example above, you are telling SQL to treat parameter a as char.
Whereas, in other example it will be treated as a varchar.
Use SQL profiler to see what is the SQL that gets executed in both the cases. That should clear it for you.
In the first case you are adding SqlParameter classes to the command. When the command is executed it is most likely generating DECLARE statements with the wrong data type. (You can verify this with a SQL trace.) If this is the case, the optimizer cannot select the correct index and falls back to a table scan.
If you use a stored proc instead, you would be forcing the parameters into the data types you declare. However, you can still do this from code if you specify the SqlDbType on the parameters.

Categories

Resources