2

I would like to make parameterized query for SQLquery that can be something like this:

SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN (1, 2, 3));

The values come from WS interface and although I can trust the consumers I want to play it safe and use parameterized query involving DbParameters to prevent SQL injection. How to do it?

Thanks!

12
  • Can you use stored procedures? Commented Mar 8, 2013 at 13:40
  • @Ankit: no I can't. I have to make existing code to handle conditions that depend on another table and it would be too big change to use stored procedures. Commented Mar 8, 2013 at 13:43
  • Do you have some code that goes with this? Commented Mar 8, 2013 at 13:45
  • which bits here are variable? is it just the IN values? Commented Mar 8, 2013 at 13:48
  • 1
    @matti well, in the example you show, the only thing suitable to be parameterized is the values 1, 2, 3. If you want to change the query in any other way, it would be good to show an example, because it could impact the most appropriate approach. Commented Mar 8, 2013 at 14:06

3 Answers 3

3

The key point, as you note, is to use parameters. IN clauses are notoriously problematic for that, annoyingly. Now, if you know the values are all integers (for example you are taking an int[] parameter to your C# method), then you can just about get away with things like:

cmd.CommandText = "SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN ("
+ string.Join(",", values) + "))"; // please don't!!!

It is horrible, suggests a very bad practice (if somebody copies it for strings, you are in a world of pain), and can't use query plan caching. You could do something like:

var sb = new StringBuilder("SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN (");
int idx = 0;
foreach(var val in values) {
    if(idx != 0) sb.Append(',');
    sb.Append("@p").Append(idx);
    cmd.Parameters.AddWithValue("@p" + idx, val);
    idx++
}
sb.Append("))");
cmd.CommandText = sb.ToString();

Which is preferable but awkward.

Or simplest: with a tool like dapper, let the library worry about it:

var data = conn.Query<YourType>(
    "SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN @values)",
    new { values });

Here dapper spots the usage and "does the right thing". It also handles the "0 values" case for you.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Marc! I'm familiar how to make parameterized queries. As I told you I was just thinking different (wrong) way. So the bottom line is to parameterize the "2nd query" just like for single table. So this did not differ in any way what I've been doing many times.
@matti absolutely not different at all; ADO.NET doesn't care what the command-text is. The only interesting technical point in the specific query you show is the IN clause - which is a real PITA to parameterize properly.
1

Here is one example:

SqlCommand cmd = new SqlCommand("SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 = @myparam", conn);

//define parameters used in command object
SqlParameter param  = new SqlParameter();
param.ParameterName = "@myparam";
param.Value         = "myvalue";

//add new parameter to command object
cmd.Parameters.Add(param);

// get data stream
reader = cmd.ExecuteReader();

Check out this link for more details: http://csharp-station.com/Tutorial/AdoDotNet/Lesson06

1 Comment

Thanks! I was thinking that I have to create a DbParameter for every value like: SELECT * FROM TABLE1 WHERE Col1 IN (@Params) which in turn would have parameters for Status-es.
1

The difficulty here is parameterizing each individual IN clause so that you can pass through a variable number of IDs.

Have a look at this useful article as to one approach to solve this: http://www.mikesdotnetting.com/Article/116/Parameterized-IN-clauses-with-ADO.NET-and-LINQ

Basically, it involves a little bit of string manipulation to build up the parameterized list of IN clauses, so you end up with a SQL statement that looks like this for your particular example with 3 IDs:

select * from TABLE1 where COL1 in (select COL2 from TABLE2 where COL3 IN (@p1, @p2, @p3));

1 Comment

Thanks. I've made code to handle the IN automatically. However I was thinking is it possible to parameterize SELECT * FROM TABLE1 WHERE Col1 IN (@Params), which in turn would have parameters for Status-es somehow. Maybe that was just stupid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.