.Net कोड से संग्रहित प्रक्रिया में टेबल वैल्यू पैरामीटर कैसे पास करें


171

मेरे पास SQL ​​Server 2005 डेटाबेस है। कुछ प्रक्रियाओं में मेरे पास तालिका पैरामीटर हैं जिन्हें मैं एक संग्रहीत खरीद के रूप में पास करता हूं nvarchar(कॉमा द्वारा अलग) और आंतरिक रूप से एकल मूल्यों में विभाजित करता हूं । मैं इसे इस तरह SQL कमांड पैरामीटर सूची में जोड़ता हूं:

cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";

मुझे डेटाबेस को SQL Server 2008 में माइग्रेट करना है। मुझे पता है कि टेबल वैल्यू पैरामीटर हैं, और मुझे पता है कि उन्हें संग्रहीत प्रक्रियाओं में कैसे उपयोग करना है। लेकिन मैं नहीं जानता कि कैसे एक SQL कमांड में पैरामीटर सूची में से एक को पास किया जाए।

क्या किसी को Parameters.Addप्रक्रिया का सही सिंटैक्स पता है ? या इस पैरामीटर को पारित करने का एक और तरीका है?


इस समाधान को देखें: EF में टेबल-वेल्यूड पैरामीटर के साथ संग्रहित प्रक्रिया। code.msdn.microsoft.com/Stored-Procedure-with-6c194514
कार्ल प्रोथमैन

इस तरह के एक मामले में, मैं आमतौर पर तारों को समतल करता हूं और उन्हें सर्वर साइड पर विभाजित करता हूं या अगर मैं एक से अधिक कॉलम करता हूं तो भी एक xml पास करता हूं। Sql यह बहुत तेज़ है जब xml प्रसंस्करण होता है। आप सभी तरीकों को आज़मा सकते हैं और प्रसंस्करण समय की जांच कर सकते हैं और उसके बाद सबसे अच्छी विधि चुन सकते हैं। एक XML <आइटम> <आइटम मूल्य = "sdadas" /> <आइटम मान = "sadsad" />...< / आइटम> जैसा दिखेगा। Sql Server पर प्रक्रिया भी सरल है। यदि आपको अधिक जानकारी की आवश्यकता है, तो इस विधि का उपयोग करके, आप हमेशा एक नया गुण जोड़ सकते हैं।
नीउ अलेक्जेंड्रू

4
@ Ni @uAlexandru, "XML को संसाधित करते समय Sql यह बहुत तेज़ है।" आस - पास भी नहीं।
नॉट्रो

जवाबों:


279

DataTable, DbDataReaderया IEnumerable<SqlDataRecord>ऑब्जेक्ट्स का उपयोग SQL Server 2008 (ADO.NET) में MSDN लेख तालिका-मान्य पैरामीटर के अनुसार तालिका-मूल्यवान पैरामीटर को पॉप्युलेट करने के लिए किया जा सकता है ।

निम्न उदाहरण एक DataTableया एक का उपयोग करके दिखाता है IEnumerable<SqlDataRecord>:

SQL कोड :

CREATE TABLE dbo.PageView
(
    PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED,
    PageViewCount BIGINT NOT NULL
);
CREATE TYPE dbo.PageViewTableType AS TABLE
(
    PageViewID BIGINT NOT NULL
);
CREATE PROCEDURE dbo.procMergePageView
    @Display dbo.PageViewTableType READONLY
AS
BEGIN
    MERGE INTO dbo.PageView AS T
    USING @Display AS S
    ON T.PageViewID = S.PageViewID
    WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1
    WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1);
END

C # कोड :

private static void ExecuteProcedure(bool useDataTable, 
                                     string connectionString, 
                                     IEnumerable<long> ids) 
{
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    {
        connection.Open();
        using (SqlCommand command = connection.CreateCommand()) 
        {
            command.CommandText = "dbo.procMergePageView";
            command.CommandType = CommandType.StoredProcedure;

            SqlParameter parameter;
            if (useDataTable) {
                parameter = command.Parameters
                              .AddWithValue("@Display", CreateDataTable(ids));
            }
            else 
            {
                parameter = command.Parameters
                              .AddWithValue("@Display", CreateSqlDataRecords(ids));
            }
            parameter.SqlDbType = SqlDbType.Structured;
            parameter.TypeName = "dbo.PageViewTableType";

            command.ExecuteNonQuery();
        }
    }
}

private static DataTable CreateDataTable(IEnumerable<long> ids) 
{
    DataTable table = new DataTable();
    table.Columns.Add("ID", typeof(long));
    foreach (long id in ids) 
    {
        table.Rows.Add(id);
    }
    return table;
}

private static IEnumerable<SqlDataRecord> CreateSqlDataRecords(IEnumerable<long> ids) 
{
    SqlMetaData[] metaData = new SqlMetaData[1];
    metaData[0] = new SqlMetaData("ID", SqlDbType.BigInt);
    SqlDataRecord record = new SqlDataRecord(metaData);
    foreach (long id in ids) 
    {
        record.SetInt64(0, id);
        yield return record;
    }
}

24
+1 उत्कृष्ट उदाहरण। टेकअवे कर रहे हैं: एक भेजने DataTableपैरामीटर मान, सेट के रूप में SqlDbTypeकरने के लिए Structuredऔर TypeNameडेटाबेस UDT नाम करने के लिए।
एलसी।

10
यदि आप किसी लूप में संदर्भ प्रकार की आवृत्ति (SqlDataRecord आपके उदाहरण में) का पुन: उपयोग करने जा रहे हैं, तो कृपया इस टिप्पणी पर एक टिप्पणी जोड़ें कि इस विशेष उदाहरण में ऐसा करना सुरक्षित क्यों है।
सोरेन बोइसन

2
यह कोड गलत है: खाली टेबल वैल्यू पैरामीटर के लिए उनका मान निर्धारित होना चाहिए null। एक खाली पैरामीटर दिए CreateSqlDataRecordsजाने पर वापस नहीं आएगा । nullids
ta.speot.is

4
@ क्रोनो: DataTable(या DataSet) केवल इसे लागू करते हैं क्योंकि उन्हें विजुअल-स्टूडियो में ड्रैग एंड ड्रॉप क्षमताओं को दबाने के लिए है, इसलिए वे IComponentकौन से कार्यान्वयन को लागू करते हैं IDisposable। यदि आप डिज़ाइनर का उपयोग नहीं करते हैं, लेकिन इसे मैन्युअल रूप से बनाते हैं तो इसका निपटान करने का कोई कारण नहीं है (या using-स्टैटमेंट का उपयोग करने के लिए )। तो यह सुनहरे नियम के अपवादों में से एक है "सब कुछ लागू करता है जो लागू होता है IDisposable"।
टिम श्मेल्टर

2
@TimSchmelter अंगूठे के एक नियम के रूप में, मैं हमेशा Disposeविधियों को कॉल करता हूं , भले ही यह केवल इतना हो कि कोड विश्लेषण मुझे चेतावनी नहीं देगा अगर मैं नहीं करता हूं। लेकिन मैं मानता हूं कि इस विशिष्ट परिदृश्य में जहां आधार DataSetऔर DataTableउदाहरणों का उपयोग किया जाता है, कॉलिंग Disposeकुछ भी नहीं करेगी।
क्रोनो

31

रयान के जवाब देने के लिए इसके अलावा आप भी स्थापित करने के लिए की आवश्यकता होगी DataColumnकी Ordinalसंपत्ति यदि आप एक साथ काम कर रहे table-valued parameterके साथ कई कॉलम जिसका ऑर्डिनल्स हैं नहीं वर्णमाला के क्रम में।

उदाहरण के लिए, यदि आपके पास निम्न तालिका मान है जो SQL में एक पैरामीटर के रूप में उपयोग किया जाता है:

CREATE TYPE NodeFilter AS TABLE (
  ID int not null
  Code nvarchar(10) not null,
);

आपको अपने कॉलम को C # में क्रमबद्ध करने की आवश्यकता होगी:

table.Columns["ID"].SetOrdinal(0);
// this also bumps Code to ordinal of 1
// if you have more than 2 cols then you would need to set more ordinals

यदि आप ऐसा करने में विफल रहते हैं, तो आपको पार्स त्रुटि मिलेगी, nvarchar को int में बदलने में विफल।


15

सामान्य

   public static DataTable ToTableValuedParameter<T, TProperty>(this IEnumerable<T> list, Func<T, TProperty> selector)
    {
        var tbl = new DataTable();
        tbl.Columns.Add("Id", typeof(T));

        foreach (var item in list)
        {
            tbl.Rows.Add(selector.Invoke(item));

        }

        return tbl;

    }

क्या आप मुझे बताएंगे कि मैं पैरामीटर के रूप में क्या करूं? फंक <टी, टीप्रोपरेटी> चयनकर्ता? क्या यह केवल tbl.Rows.Add (आइटम) नहीं हो सकता है और उस पैरामीटर की कोई आवश्यकता नहीं है।
GDroid

selector.Invoke (आइटम) उस आइटम पर संपत्ति का चयन करता है जो अधिकांश मामलों में एक int है, लेकिन यह आपको एक स्ट्रिंग संपत्ति का चयन करने की भी अनुमति देता है
Martea

क्या आप कृपया एक उदाहरण प्रदान कर सकते हैं कि मैं वहाँ कैसे चयनकर्ता रखूँ ?? मेरे पास एक सूची है <गाइड> संग्रहीत खरीद के लिए पारित करने के लिए ...
GDroid

guideList.ToTabledValuedParameter (x => x), चूंकि x आपके मामले में मार्गदर्शिका है, इसलिए रिटर्न एक कॉलम के साथ एक
डेटाटैब

5

इसके साथ काम करने का सबसे साफ तरीका। मान लें कि आपकी तालिका पूर्णांक की एक सूची है जिसे "dbo.tvp_Int" कहा जाता है (अपने स्वयं के टेबल प्रकार के लिए अनुकूलित करें)

यह एक्सटेंशन विधि बनाएं ...

public static void AddWithValue_Tvp_Int(this SqlParameterCollection paramCollection, string parameterName, List<int> data)
{
   if(paramCollection != null)
   {
       var p = paramCollection.Add(parameterName, SqlDbType.Structured);
       p.TypeName = "dbo.tvp_Int";
       DataTable _dt = new DataTable() {Columns = {"Value"}};
       data.ForEach(value => _dt.Rows.Add(value));
       p.Value = _dt;
   }
}

अब आप ऐसा करने से कहीं भी एक लाइन में एक टेबल वैल्यू पैरामीटर जोड़ सकते हैं:

cmd.Parameters.AddWithValueFor_Tvp_Int("@IDValues", listOfIds);

1
क्या होगा यदि पैरामोल्केशन NULL है? खाली टाइप कैसे पास करें?
मुफ्लिक्स

2
@ मुफ़्लिक्स अस्पष्ट रूप से, विस्तार विधियाँ वास्तव में अशक्त उदाहरणों के विरुद्ध काम करती हैं। इसलिए if(paramCollection != null)विधि के शीर्ष पर एक साधारण चेक जोड़ना ठीक होगा
Rhumborl

1
प्रारंभिक उत्तर के साथ अद्यतित उत्तर- चेक
शहज़ाद कुरैशी

2
शायद थोड़ा पांडित्यपूर्ण, लेकिन मैं हस्ताक्षर के IEnumerableबजाय उपयोग करूंगा List, इस तरह से आप कुछ भी पारित कर सकते हैं IEnumerable, न कि केवल सूचियों के लिए, क्योंकि आप किसी विशेष फ़ंक्शन का उपयोग नहीं कर रहे हैं List, मैं वास्तव में कोई कारण नहीं देखता हूं usIEnumerable
फ्रांसिस लॉर्ड

सूची का उपयोग करना आपको शॉर्टहैंड डेटा का उपयोग करने की अनुमति देता है।फोरएच (), अन्यथा आपको वास्तव में फॉरच लूप लिखना होगा। जो काम भी कर सकता था, लेकिन मुझे यथासंभव छोटी चीजें लिखना पसंद है।
शहजाद कुरैशी

0

अपने प्रकार से उपयुक्त पैरामीटर बनाने के लिए इस कोड का उपयोग करें:

private SqlParameter GenerateTypedParameter(string name, object typedParameter)
{
    DataTable dt = new DataTable();

    var properties = typedParameter.GetType().GetProperties().ToList();
    properties.ForEach(p =>
    {
        dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
    });
    var row = dt.NewRow();
    properties.ForEach(p => { row[p.Name] = (p.GetValue(typedParameter) ?? DBNull.Value); });
    dt.Rows.Add(row);

    return new SqlParameter
    {
        Direction = ParameterDirection.Input,
        ParameterName = name,
        Value = dt,
        SqlDbType = SqlDbType.Structured
    };
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.