SqlCommand में ऐरे पैरामीटर पास करें


144

मैं नीचे सी # की तरह SQL कमोड में सरणी पैरामीटर पास करने की कोशिश कर रहा हूं, लेकिन यह काम नहीं करता है। क्या कोई इससे पहले मिलता है?

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);
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
     if (item.Selected)
     {
         sb.Append(item.Text + ",");
     }
}

sqlComm.Parameters["@Age"].Value = sb.ToString().TrimEnd(',');

11
वास्तव में विषय नहीं है, लेकिन यह मुझे ऐसा लगता है कि जैसे किसी तालिका में स्तंभ के रूप में आयु एक बुरा विचार है, क्योंकि इसे लगातार अद्यतन करने की आवश्यकता होगी। लोग बूढ़े हो जाते हैं? हो सकता है कि आपको इसके बजाय एक तारीख DateOfBirth होने पर विचार करना चाहिए?
Kjetil Watnedal

यहाँ अच्छे उत्तर के साथ सवाल: stackoverflow.com/questions/83471/…
एडम बटलर

जवाबों:


169

आपको एक बार में सरणी में मान जोड़ना होगा।

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);

अद्यतन: यहाँ एक विस्तारित और पुन: प्रयोज्य समाधान है जो अपने सुझाए गए संपादन के साथ एडम के उत्तर का उपयोग करता है। मैंने इसे थोड़ा सुधार दिया और इसे कॉल करने के लिए और भी आसान बनाने के लिए इसे एक विस्तार विधि बना दिया।

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();
    }

}

इसे इस तरह कहा जाता है ...

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

ध्यान दें "{आयु}" एसक्यूएल स्टेटमेंट में वही पैरामीटर नाम है जो हम AddArrayParameters को भेज रहे हैं। AddArrayParameters मान को सही मापदंडों के साथ बदल देगा।


11
क्या इस पद्धति में सुरक्षा समस्या है, जैसे कि sql इंजेक्शन?
योंगवेई जिंग

7
क्योंकि आप मानों को मापदंडों में डाल रहे हैं इसलिए एसक्यूएल इंजेक्शन का कोई खतरा नहीं है।
ब्रायन

यह वही है जिसकी मुझे तलाश थी लेकिन मेरा एक सवाल था। यदि ओपी में एसक्यूएल को जोड़ने के लिए समान कॉलम होते हैं, तो हम यह कैसे करेंगे? उदाहरण। तालिका से चुनें * जहां आयु = ({0}) या आयु = ({1})। (हम इसे cmd.Parameters के साथ कैसे करेंगे)
कोको देव

1
@ T2Tom, वूप्स। मैंने ठीक कर दिया। धन्यवाद।
ब्रायन

1
मुझे यह पसंद है, और केवल प्लेसहोल्डर स्ट्रिंग को एक चर में निकालने के बाद निम्न संशोधन किया गया है: var paramPlaceholder = "{" & paramNameRoot & "}"; Debug.Assert(cmd.CommandText.Contains(paramPlaceholder), "Parameter Name Root must exist in the Source Query"); यह देवों की मदद करना चाहिए यदि वे क्वेरी के साथ paramNameRoot से मेल खाना भूल जाते हैं।
मिकाल्ट

37

मैं इस उत्तर पर विस्तार करना चाहता था कि ब्रायन ने अन्य स्थानों पर इसे आसानी से प्रयोग करने योग्य बनाने में योगदान दिया।

/// <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);
}

आप इस नए फ़ंक्शन का उपयोग इस प्रकार कर सकते हैं:

SqlCommand cmd = new SqlCommand();

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

cmd.CommandText = sql;


संपादित करें: यहां एक सामान्य बदलाव है जो किसी भी प्रकार के मूल्यों की एक सरणी के साथ काम करता है और विस्तार विधि के रूप में उपयोग करने योग्य है:

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); 
    }
}

फिर आप इस विस्तार विधि का उपयोग इस प्रकार कर सकते हैं:

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);

AddArrayParameters को कॉल करने से पहले सुनिश्चित करें कि आप CommandText सेट करें।

यह भी सुनिश्चित करें कि आपका पैरामीटर नाम आंशिक रूप से आपके कथन में कुछ और मेल नहीं करेगा (यानी @AgeOfChild)


1
यहाँ एक सामान्य परिवर्तन है जो किसी भी प्रकार के मानों के सरणी के साथ काम करता है और एक विस्तार विधि के रूप में प्रयोग करने योग्य है: सार्वजनिक स्थैतिक शून्य AddArrayParameters <T> (यह SqlCommand cmd, string name, IEnumableable <T> मान) {var name = string.Join (",", मान। सेलेक्ट करें ((मान, i) => {var paramName = name + *; cmd.Parameters.AddWithValue (paramName, value); वापसी paramName;};}); cmd.CommandText = cmd.CommandText.Replace (नाम, नाम); }
एडम नेमिटॉफ

इस जवाब के साथ मामूली मुद्दा AddWithValueफ़ंक्शन के साथ है , कोई भी मौका जिसे आप ठीक कर सकते हैं?
डेविड जी

यह उत्तर गलत है क्योंकि इसमें खराब मापनीयता और प्रदर्शन है और यह खराब कोडिंग प्रथाओं को बढ़ावा देता है।
इगोर लेविकी

24

यदि आप "डैपर" जैसे उपकरण का उपयोग कर सकते हैं, तो यह बस हो सकता है:

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();

साफ-सुथरी व्यक्ति मापदंडों को यह unwrapping संभाल लेंगे आप के लिए


निर्भरता :( डैप्पर खींचतान बहुत सारे
एम एल टी

@ एमएलटी हुह? नहीं, यह नहीं है; नेटफैक्स पर: "कोई निर्भरता नहीं"; ns2.0 पर, बस "System.Reflection.Emit.Lightweight" - और हम शायद निकाल सकते हैं कि अगर हम एक necroreapp लक्ष्य जोड़ा
मार्क Gravell

मुझे चर्चा को हाईजैक करने का मतलब नहीं था, लेकिन मैंने किया ... इस प्रकार अब तक मैं Npgsql का उपयोग करता हूं जो '{1,2,3}'एक फ़ंक्शन में शैली तर्क के रूप में सरणियों को ठीक से संभालता है (न कि जहां में एक खंड में), लेकिन मैं सादे ODBC का उपयोग करता हूं यदि नहीं सरणी परेशानी। मुझे लगता है कि मुझे इस मामले में Dapper ODBC की भी आवश्यकता होगी। यहाँ है कि यह क्या खींचना चाहता है। snipboard.io/HU0RpJ.jpg । शायद मैं पर साफ-सुथरी ... और पर पढ़ना चाहिए
एम एल टी

16

यदि आप MS SQL Server 2008 और इसके बाद के संस्करण का उपयोग कर रहे हैं, तो आप यहां बताए गए तालिका-मानदंड जैसे http://www.sommarskog.se/arrays-in-sql-2008.html का उपयोग कर सकते हैं ।

1. आपके द्वारा उपयोग किए जा रहे प्रत्येक पैरामीटर प्रकार के लिए एक तालिका प्रकार बनाएं

निम्न आदेश पूर्णांक के लिए एक तालिका प्रकार बनाता है:

create type int32_id_list as table (id int not null primary key)

2. सहायक विधियों को लागू करें

public static SqlCommand AddParameter<T>(this SqlCommand command, string name, IEnumerable<T> ids)
{
  var parameter = command.CreateParameter();      

  parameter.ParameterName = name;
  parameter.TypeName = typeof(T).Name.ToLowerInvariant() + "_id_list";
  parameter.SqlDbType = SqlDbType.Structured;
  parameter.Direction = ParameterDirection.Input;

  parameter.Value = CreateIdList(ids);

  command.Parameters.Add(parameter);
  return command;
}

private static DataTable CreateIdList<T>(IEnumerable<T> ids)
{
  var table = new DataTable();
  table.Columns.Add("id", typeof (T));

  foreach (var id in ids)
  {
    table.Rows.Add(id);
  }

  return table;
}

3. इसे इस तरह इस्तेमाल करें

cmd.CommandText = "select * from TableA where Age in (select id from @age)"; 
cmd.AddParameter("@age", new [] {1,2,3,4,5});

1
सोनारक्यूब का उपयोग करते समय लाइन table.Rows.Add(id);एक मामूली कोड गंध होती है। मैं foreach के अंदर इस विकल्प का प्रयोग किया: var row = table.NewRow(); row["id"] = id; table.Rows.Add(row);
पोगोस्मा

1
यह स्वीकृत उत्तर होना चाहिए, खासकर यदि इसे अधिक कॉलम स्वीकार करने के लिए अनुकूलित किया गया था।
इगोर लेविक

10

चूंकि एक विधि है

SqlCommand.Parameters.AddWithValue(parameterName, value)

मानों की सूची को बदलने के लिए एक पैरामीटर (नाम) को स्वीकार करने वाली विधि बनाने के लिए यह अधिक सुविधाजनक हो सकता है। यह पैरामीटर स्तर ( AddWithValue की तरह ) पर नहीं है, लेकिन कमांड पर ही है इसलिए इसे AddParametersWithValues और न केवल AddWithValues कहना बेहतर है :

क्वेरी:

SELECT * from TableA WHERE Age IN (@age)

उपयोग:

sqlCommand.AddParametersWithValues("@age", 1, 2, 3);

विस्तार विधि:

public static class SqlCommandExtensions
{
    public static void AddParametersWithValues<T>(this SqlCommand cmd,  string parameterName, params T[] values)
    {
        var parameterNames = new List<string>();
        for(int i = 0; i < values.Count(); i++)
        {
            var paramName = @"@param" + i;
            cmd.Parameters.AddWithValue(paramName, values.ElementAt(i));
            parameterNames.Add(paramName);
        }

        cmd.CommandText = cmd.CommandText.Replace(parameterName, string.Join(",", parameterNames));
    }
}

1
ऐसा लगता है कि इस विस्तार पद्धति के कई पुनरावृत्तियों कुछ उत्तरों में मौजूद हैं। हालांकि, मैंने इसका इस्तेमाल किया, इसलिए मैं इसे वोट कर रहा हूं :-)
Dan Forbes

पैरामीटर नाम के लिए एक स्थैतिक सूचकांक का उपयोग करना बेहतर है
shmnff

6

मैं एक और तरीका प्रस्तावित करना चाहता हूं, IN ऑपरेटर के साथ सीमा को कैसे हल किया जाए।

उदाहरण के लिए हमारे पास निम्नलिखित प्रश्न हैं

select *
from Users U
WHERE U.ID in (@ids)

हम उपयोगकर्ताओं को फ़िल्टर करने के लिए कई आईडी पास करना चाहते हैं। दुर्भाग्य से यह आसान तरीके से C # के साथ करना संभव नहीं है। लेकिन मेरे पास "string_split" फ़ंक्शन का उपयोग करके इसके लिए फाउंटेन वर्कआउट है। हमें निम्नलिखित के लिए अपनी क्वेरी को फिर से लिखना होगा।

declare @ids nvarchar(max) = '1,2,3'

SELECT *
FROM Users as U
CROSS APPLY string_split(@ids, ',') as UIDS
WHERE U.ID = UIDS.value

अब हम आसानी से अल्पविराम द्वारा अलग किए गए मानों के एक पैरामीटर गणना को पास कर सकते हैं।


सबसे अच्छा और सबसे अच्छा तरीका मैंने पाया है, बशर्ते आपकी अनुकूलता वर्तमान हो।
user1040975

4

आइटमों की एक सरणी को एक ढहने वाले पैरामीटर के रूप में WHERE..IN में पारित करना विफल हो जाएगा क्योंकि क्वेरी का रूप ले लेगा WHERE Age IN ("11, 13, 14, 16")

लेकिन आप अपने पैरामीटर को एक्सएमएल या जेएसएन में क्रमबद्ध सरणी के रूप में पास कर सकते हैं:

nodes()विधि का उपयोग:

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();

OPENXMLविधि का उपयोग:

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();

यह एसक्यूएल पक्ष पर थोड़ा अधिक है और आपको एक उचित XML (रूट के साथ) की आवश्यकता है।

OPENJSONविधि का उपयोग करना (SQL सर्वर 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);

ध्यान दें कि अंतिम विधि के लिए आपको 130+ पर संगतता स्तर भी होना चाहिए।


0

अवलोकन: पैरामीटर प्रकार सेट करने के लिए DbType का उपयोग करें।

var parameter = new SqlParameter();
parameter.ParameterName = "@UserID";
parameter.DbType = DbType.Int32;
parameter.Value = userID.ToString();

var command = conn.CreateCommand()
command.Parameters.Add(parameter);
var reader = await command.ExecuteReaderAsync()

-1

का उपयोग करें .AddWithValue(), तो:

sqlComm.Parameters.AddWithValue("@Age", sb.ToString().TrimEnd(','));

वैकल्पिक रूप से, आप इसका उपयोग कर सकते हैं:

sqlComm.Parameters.Add(
    new SqlParameter("@Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar }
    );

आपका कुल कोड नमूना निम्न प्रकार से दिखेगा:

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;

StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
     if (item.Selected)
     {
         sb.Append(item.Text + ",");
     }
}

sqlComm.Parameters.AddWithValue("@Age", sb.ToString().TrimEnd(','));

// OR

// sqlComm.Parameters.Add(new SqlParameter("@Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar });

आयु क्षेत्र का प्रकार nvchar है int नहीं। फर्क पड़ता है क्या?
योंगवेई जिंग

यह नहीं करना चाहिए विशेषकर दूसरी विधि से। आप स्पष्ट रूप से प्रकार निर्दिष्ट करें।
काइल रोसेन्डो

मैं दोनों विधि का उपयोग करता हूं, यह अभी भी काम नहीं करता है। मैं स्ट्रिंग में हेरफेर नहीं करना चाहता जो सुरक्षा समस्या का कारण बन सकता है
योंगवेई जिंग

मैं वास्तव में आपको समझ नहीं रहा हूं। जब आप कहते हैं कि यह काम नहीं करता है, क्या यह एक अपवाद नहीं है? यह क्या करता है?
काइल रोसेन्डो

1
यह अपवाद नहीं फेंकता है, यह कुछ भी नहीं लौटाता है। लेकिन मैं स्टूडियो प्रबंधन में टी-एसक्यूएल चलाता हूं, यह कई परिणाम देता है।
योंगवेई जिंग

-1

यहाँ ब्रायन के उत्तर का एक मामूली रूप है जो किसी और को उपयोगी लग सकता है। चाबियों की एक सूची लेता है और इसे पैरामीटर सूची में छोड़ देता है।

//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(',') + ")";

यह उत्तर गलत है क्योंकि इसमें खराब मापनीयता और प्रदर्शन है और यह खराब कोडिंग प्रथाओं को बढ़ावा देता है।
इगोर लेविकी

-3

प्रयत्न

sqlComm.Parameters["@Age"].Value = sb.ToString().Replace(","," ");

-5

इसे इस तरह आज़माएं

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(',');
     } 
} 

2
SqlConnection और SqlCommnad को लूप में क्यों रखा जाता है?
योंगवेई जिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.