Click here to Skip to main content
15,890,512 members
Articles / Database Development / SQL Server
Tip/Trick

Passing NULL and table values to a parametrized SQL query

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
4 Jun 2013CPOL1 min read 19.1K   3  
The importance of using parameterized SQL queries has been well established, but passing table values or occassional NULL values has always been a problem ... until now ...

Introduction

This tip demonstrates how to create a universal method to pass null and table values to dynamic, but parametrized SQL queries, without a need to create any special types on the server side, or relying on pattern matching for IN clauses. While it returns a DataSet, it can be adopted to return ResultSet<T> or a value type, which exercise is left to the reader.

Background

The importance of using parametrized SQL queries, has been discussed many times, and it is well established that it helps in increasing application security and performance. Security, because it prevents SQL injection attacks. Performance, because SQL will automatically cache execution plans created. It is recommended that the reader will investigate these two subjects at their own pace.

Using the code

Below you will find an example of a method expecting an SqlConnectionStringBuilder, an SQL statement as a string, and a variable length array of parameters to be passed as arguments to the SQL. The method expects the parameters to be "indexed", meaning that the position of the value in params array, dictates the parameter name, for example, @0 for first item in the array, @1 for second, etc. It would be even easier to enforce IDictionary<TKey, TValue> instead of object[], but, quite a few times this solution comes quite handy, if the number of parameters is unknown, but their order is given.

C#
private static DataSet GetDataSet(
   SqlConnectionStringBuilder scsb, string strSql, params object[] pars)
{
    var ds = new DataSet();
    using (var sqlConn = new SqlConnection(scsb.ConnectionString))
    {
        var sqlParameters = new List<SqlParameter>();
        var replacementStrings = new Dictionary<string, string>();
        if (pars != null)
        {
            for (int i = 0; i < pars.Length; i++)
            {
                if (pars[i] is IEnumerable<object>)
                {
                    List<object> enumerable = (pars[i] as IEnumerable<object>).ToList();
                    replacementStrings.Add("@" + i, String.Join(",", 
                      enumerable.Select((value, pos) => String.Format("@_{0}_{1}", i, pos))));
                    sqlParameters.AddRange(enumerable.Select((value, pos) => 
                      new SqlParameter(String.Format("@_{0}_{1}", i, pos), 
                      value ?? DBNull.Value)).ToArray());
                }
                else
                {
                    sqlParameters.Add(new SqlParameter(
                       String.Format("@{0}", i), pars[i] ?? DBNull.Value));
                }
            }
        }
        strSql = replacementStrings.Aggregate(strSql, (current, replacementString) => 
          current.Replace(replacementString.Key, replacementString.Value));
        using (var sqlCommand = new SqlCommand(strSql, sqlConn))
        {
            if (pars != null)
            {
                sqlCommand.Parameters.AddRange(sqlParameters.ToArray());
            }
            else
            {
                //Fail-safe, just in case a user intends to pass a single null parameter
                sqlCommand.Parameters.Add(new SqlParameter("@0", DBNull.Value));
            }
            using (var sqlDataAdapter = new SqlDataAdapter(sqlCommand))
            {
                sqlDataAdapter.Fill(ds);
            }
        }
    }
    return ds;
}

The method also implements a fail-safe mechanism in scenarios when there is a single parameter and its value is NULL. It accepts any combination of values, table values and null values, without a requirement to create any special types, functions, or dynamic joins. Error handling is not in scope for this solution.

Sample use:

C#
var scsb = new SqlConnectionStringBuilder { DataSource = "localhost", IntegratedSecurity = true };
string sqlStr = "SELECT * FROM LOGTABLE WHERE LEVEL IN (@0) AND APP = @1";
DataSet result = GetDataSet(scsb, sqlStr, new List<string> { "ERROR", "WARN" }, "PROD"); 

 Enjoy.

History  

  • Initial version posted on 6/4/2013.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect BI Software, Inc.
United States United States
A seasoned IT Professional. Programming and data processing artist. Contributor to StackOverflow.

Comments and Discussions

 
-- There are no messages in this forum --