Pass Array Parameter in SqlCommand

前端 未结 12 1890
情深已故
情深已故 2020-11-22 08:27

I am trying to pass array parameter to SQL commnd in C# like below, but it does not work. Does anyone meet it before?

string sqlCommand = \"SELECT * from Ta         


        
相关标签:
12条回答
  • 2020-11-22 09:18

    Here is a minor variant of Brian's answer that someone else may find useful. Takes a List of keys and drops it into the parameter list.

    //keyList is a List<string>
    System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand();
    string sql = "SELECT fieldList FROM dbo.tableName WHERE keyField in (";
    int i = 1;
    foreach (string key in keyList) {
        sql = sql + "@key" + i + ",";
        command.Parameters.AddWithValue("@key" + i, key);
        i++;
    }
    sql = sql.TrimEnd(',') + ")";
    
    0 讨论(0)
  • 2020-11-22 09:21

    If you can use a tool like "dapper", this can be simply:

    int[] ages = { 20, 21, 22 }; // could be any common list-like type
    var rows = connection.Query<YourType>("SELECT * from TableA WHERE Age IN @ages",
              new { ages }).ToList();
    

    Dapper will handle unwrapping this to individual parameters for you.

    0 讨论(0)
  • 2020-11-22 09:22

    I wanted to expand on the answer that Brian contributed to make this easily usable in other places.

    /// <summary>
    /// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
    /// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN (returnValue))
    /// </summary>
    /// <param name="sqlCommand">The SqlCommand object to add parameters to.</param>
    /// <param name="array">The array of strings that need to be added as parameters.</param>
    /// <param name="paramName">What the parameter should be named.</param>
    protected string AddArrayParameters(SqlCommand sqlCommand, string[] array, string paramName)
    {
        /* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually. 
         * Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
         * IN statement in the CommandText.
         */
        var parameters = new string[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            parameters[i] = string.Format("@{0}{1}", paramName, i);
            sqlCommand.Parameters.AddWithValue(parameters[i], array[i]);
        }
    
        return string.Join(", ", parameters);
    }
    

    You can use this new function as follows:

    SqlCommand cmd = new SqlCommand();
    
    string ageParameters = AddArrayParameters(cmd, agesArray, "Age");
    sql = string.Format("SELECT * FROM TableA WHERE Age IN ({0})", ageParameters);
    
    cmd.CommandText = sql;
    


    Edit: Here is a generic variation that works with an array of values of any type and is usable as an extension method:

    public static class Extensions
    {
        public static void AddArrayParameters<T>(this SqlCommand cmd, string name, IEnumerable<T> values) 
        { 
            name = name.StartsWith("@") ? name : "@" + name;
            var names = string.Join(", ", values.Select((value, i) => { 
                var paramName = name + i; 
                cmd.Parameters.AddWithValue(paramName, value); 
                return paramName; 
            })); 
            cmd.CommandText = cmd.CommandText.Replace(name, names); 
        }
    }
    

    You can then use this extension method as follows:

    var ageList = new List<int> { 1, 3, 5, 7, 9, 11 };
    var cmd = new SqlCommand();
    cmd.CommandText = "SELECT * FROM MyTable WHERE Age IN (@Age)";    
    cmd.AddArrayParameters("Age", ageList);
    

    Make sure you set the CommandText before calling AddArrayParameters.

    Also make sure your parameter name won't partially match anything else in your statement (i.e. @AgeOfChild)

    0 讨论(0)
  • 2020-11-22 09:25

    You will need to add the values in the array one at a time.

    var parameters = new string[items.Length];
    var cmd = new SqlCommand();
    for (int i = 0; i < items.Length; i++)
    {
        parameters[i] = string.Format("@Age{0}", i);
        cmd.Parameters.AddWithValue(parameters[i], items[i]);
    }
    
    cmd.CommandText = string.Format("SELECT * from TableA WHERE Age IN ({0})", string.Join(", ", parameters));
    cmd.Connection = new SqlConnection(connStr);
    

    UPDATE: Here is an extended and reusable solution that uses Adam's answer along with his suggested edit. I improved it a bit and made it an extension method to make it even easier to call.

    public static class SqlCommandExt
    {
    
        /// <summary>
        /// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
        /// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN ({paramNameRoot}))
        /// </summary>
        /// <param name="cmd">The SqlCommand object to add parameters to.</param>
        /// <param name="paramNameRoot">What the parameter should be named followed by a unique value for each value. This value surrounded by {} in the CommandText will be replaced.</param>
        /// <param name="values">The array of strings that need to be added as parameters.</param>
        /// <param name="dbType">One of the System.Data.SqlDbType values. If null, determines type based on T.</param>
        /// <param name="size">The maximum size, in bytes, of the data within the column. The default value is inferred from the parameter value.</param>
        public static SqlParameter[] AddArrayParameters<T>(this SqlCommand cmd, string paramNameRoot, IEnumerable<T> values, SqlDbType? dbType = null, int? size = null)
        {
            /* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually. 
             * Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
             * IN statement in the CommandText.
             */
            var parameters = new List<SqlParameter>();
            var parameterNames = new List<string>();
            var paramNbr = 1;
            foreach (var value in values)
            {
                var paramName = string.Format("@{0}{1}", paramNameRoot, paramNbr++);
                parameterNames.Add(paramName);
                SqlParameter p = new SqlParameter(paramName, value);
                if (dbType.HasValue)
                    p.SqlDbType = dbType.Value;
                if (size.HasValue)
                    p.Size = size.Value;
                cmd.Parameters.Add(p);
                parameters.Add(p);
            }
    
            cmd.CommandText = cmd.CommandText.Replace("{" + paramNameRoot + "}", string.Join(",", parameterNames));
    
            return parameters.ToArray();
        }
    
    }
    

    It is called like this...

    var cmd = new SqlCommand("SELECT * FROM TableA WHERE Age IN ({Age})");
    cmd.AddArrayParameters("Age", new int[] { 1, 2, 3 });
    

    Notice the "{Age}" in the sql statement is the same as the parameter name we are sending to AddArrayParameters. AddArrayParameters will replace the value with the correct parameters.

    0 讨论(0)
  • 2020-11-22 09:26

    Passing an array of items as a collapsed parameter to the WHERE..IN clause will fail since query will take form of WHERE Age IN ("11, 13, 14, 16").

    But you can pass your parameter as an array serialized to XML or JSON:

    Using nodes() method:

    StringBuilder sb = new StringBuilder();
    
    foreach (ListItem item in ddlAge.Items)
      if (item.Selected)
        sb.Append("<age>" + item.Text + "</age>"); // actually it's xml-ish
    
    sqlComm.CommandText = @"SELECT * from TableA WHERE Age IN (
        SELECT Tab.col.value('.', 'int') as Age from @Ages.nodes('/age') as Tab(col))";
    sqlComm.Parameters.Add("@Ages", SqlDbType.NVarChar);
    sqlComm.Parameters["@Ages"].Value = sb.ToString();
    

    Using OPENXML method:

    using System.Xml.Linq;
    ...
    XElement xml = new XElement("Ages");
    
    foreach (ListItem item in ddlAge.Items)
      if (item.Selected)
        xml.Add(new XElement("age", item.Text);
    
    sqlComm.CommandText = @"DECLARE @idoc int;
        EXEC sp_xml_preparedocument @idoc OUTPUT, @Ages;
        SELECT * from TableA WHERE Age IN (
        SELECT Age from OPENXML(@idoc, '/Ages/age') with (Age int 'text()')
        EXEC sp_xml_removedocument @idoc";
    sqlComm.Parameters.Add("@Ages", SqlDbType.Xml);
    sqlComm.Parameters["@Ages"].Value = xml.ToString();
    

    That's a bit more on the SQL side and you need a proper XML (with root).

    Using OPENJSON method (SQL Server 2016+):

    using Newtonsoft.Json;
    ...
    List<string> ages = new List<string>();
    
    foreach (ListItem item in ddlAge.Items)
      if (item.Selected)
        ages.Add(item.Text);
    
    sqlComm.CommandText = @"SELECT * from TableA WHERE Age IN (
        select value from OPENJSON(@Ages))";
    sqlComm.Parameters.Add("@Ages", SqlDbType.NVarChar);
    sqlComm.Parameters["@Ages"].Value = JsonConvert.SerializeObject(ages);
    

    Note that for the last method you also need to have Compatibility Level at 130+.

    0 讨论(0)
  • 2020-11-22 09:29

    try it like this

    StringBuilder sb = new StringBuilder(); 
    foreach (ListItem item in ddlAge.Items) 
    { 
         if (item.Selected) 
         { 
              string sqlCommand = "SELECT * from TableA WHERE Age IN (@Age)"; 
              SqlConnection sqlCon = new SqlConnection(connectString); 
              SqlCommand sqlComm = new SqlCommand(); 
              sqlComm.Connection = sqlCon; 
              sqlComm.CommandType = System.Data.CommandType.Text; 
              sqlComm.CommandText = sqlCommand; 
              sqlComm.CommandTimeout = 300; 
              sqlComm.Parameters.Add("@Age", SqlDbType.NVarChar);
              sb.Append(item.Text + ","); 
              sqlComm.Parameters["@Age"].Value = sb.ToString().TrimEnd(',');
         } 
    } 
    
    0 讨论(0)
提交回复
热议问题