Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameterize an SQL IN clause

How do I parameterize a query containing an IN clause with a variable number of arguments, like this one?

SELECT * FROM Tags  WHERE Name IN ('ruby','rails','scruffy','rubyonrails') ORDER BY Count DESC 

In this query, the number of arguments could be anywhere from 1 to 5.

I would prefer not to use a dedicated stored procedure for this (or XML), but if there is some elegant way specific to SQL Server 2008, I am open to that.

like image 294
Jeff Atwood Avatar asked Dec 03 '08 16:12

Jeff Atwood


People also ask

Can we pass parameter in with clause?

Solution 1. You can use parameters in a WITH clause just like in a traditional statement. The problem in the example is the IN operator which requires a list of values.

How do I create a parameterized SQL query?

Declare statements start with the keyword DECLARE , followed by the name of the parameter (starting with a question mark) followed by the type of the parameter and an optional default value. The default value must be a literal value, either STRING , NUMERIC , BOOLEAN , DATE , or TIME .

How do you parameterize a query?

The first way to parameterize a query is by mapping the query. To map a parameter the first thing you need to do is add a parameter mapping from the Parameters tab. Then find the value you want map the parameter to, select the variable and hit OK. You have now mapped your parameter to the Expected Query Value.


1 Answers

You can parameterize each value, so something like:

string[] tags = new string[] { "ruby", "rails", "scruffy", "rubyonrails" }; string cmdText = "SELECT * FROM Tags WHERE Name IN ({0})";  string[] paramNames = tags.Select(     (s, i) => "@tag" + i.ToString() ).ToArray();  string inClause = string.Join(", ", paramNames); using (SqlCommand cmd = new SqlCommand(string.Format(cmdText, inClause))) {     for(int i = 0; i < paramNames.Length; i++) {        cmd.Parameters.AddWithValue(paramNames[i], tags[i]);     } } 

Which will give you:

cmd.CommandText = "SELECT * FROM Tags WHERE Name IN (@tag0, @tag1, @tag2, @tag3)" cmd.Parameters["@tag0"] = "ruby" cmd.Parameters["@tag1"] = "rails" cmd.Parameters["@tag2"] = "scruffy" cmd.Parameters["@tag3"] = "rubyonrails" 

No, this is not open to SQL injection. The only injected text into CommandText is not based on user input. It's solely based on the hardcoded "@tag" prefix, and the index of an array. The index will always be an integer, is not user generated, and is safe.

The user inputted values are still stuffed into parameters, so there is no vulnerability there.

Edit:

Injection concerns aside, take care to note that constructing the command text to accomodate a variable number of parameters (as above) impede's SQL server's ability to take advantage of cached queries. The net result is that you almost certainly lose the value of using parameters in the first place (as opposed to merely inserting the predicate strings into the SQL itself).

Not that cached query plans aren't valuable, but IMO this query isn't nearly complicated enough to see much benefit from it. While the compilation costs may approach (or even exceed) the execution costs, you're still talking milliseconds.

If you have enough RAM, I'd expect SQL Server would probably cache a plan for the common counts of parameters as well. I suppose you could always add five parameters, and let the unspecified tags be NULL - the query plan should be the same, but it seems pretty ugly to me and I'm not sure that it'd worth the micro-optimization (although, on Stack Overflow - it may very well be worth it).

Also, SQL Server 7 and later will auto-parameterize queries, so using parameters isn't really necessary from a performance standpoint - it is, however, critical from a security standpoint - especially with user inputted data like this.

like image 123
Mark Brackett Avatar answered Nov 10 '22 21:11

Mark Brackett