Dapper ORM से X WHERE id IN (…) का चयन करें


231

जब व्यावसायिक तर्क से IN खंड के लिए मानों की सूची आ रही है, तो Dapper ORM का उपयोग करके IN खंड के साथ क्वेरी लिखने का सबसे अच्छा तरीका क्या है? उदाहरण के लिए मान लें कि मेरे पास एक प्रश्न है:

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

commaSeparatedListOfIDsव्यापार तर्क से भेजी जा रही है और यह किसी भी प्रकार का हो सकता है IEnumerable(of Integer)। मैं इस मामले में एक क्वेरी कैसे बनाऊंगा? क्या मुझे ऐसा करना है जो मैं अभी तक कर रहा हूं जो कि मूल रूप से स्ट्रिंग संघनन है या क्या उन्नत पैरामीटर मैपिंग तकनीक के कुछ प्रकार हैं जिनके बारे में मुझे जानकारी नहीं है?

जवाबों:


366

डैपर सीधे इसका समर्थन करता है। उदाहरण के लिए...

string sql = "SELECT * FROM SomeTable WHERE id IN @ids"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});

47
मुझे लगता है कि यह ध्यान रखना महत्वपूर्ण है कि आप अपने सरणी में कितने आइटम भेज सकते हैं, इसकी एक सीमित सीमा है। जब मैंने बहुत से आईडी में पास किया तो मुझे यह कठिन रास्ता मालूम हुआ। मुझे सटीक संख्या याद नहीं है, लेकिन मेरी याद से मुझे लगता है कि यह 200 तत्वों से पहले डॅपर काम करना / क्वेरी को निष्पादित करना बंद कर देता है।
मार्को

8
मार्को, कि आईएस महत्वपूर्ण है। और, यदि आप इसे इस तरह से कर रहे हैं, तो आप अपने डेटा को क्वेरी करने का एक और तरीका खोजने पर विचार कर सकते हैं, जैसे कि आईडी की सूची को पारित करने के बजाय ज्वाइन या एंटी-जॉइन करना। IN क्लॉज सबसे उच्च प्रदर्शन करने वाली क्वेरी नहीं है और इसे अक्सर एक मौजूद क्लॉज द्वारा प्रतिस्थापित किया जा सकता है, जो तेज होगा।
डॉन रोलिंग

24
FYI करें - SQL Server 2008 R2 में INक्लॉज पर 2100 प्रविष्टियों की सीमा है ।
जेसी

6
और SQLite में 999 चर की एक डिफ़ॉल्ट सीमा है।
कैमरून

8
खबरदार: SQL सर्वर में यह विफल रहता है यदि आपके सरणी में कई आइटम हैं और आप कोष्ठक में पैरामीटर लपेटते हैं। कोष्ठक हटाने से समस्या ठीक हो जाएगी।
अंजबवेन

66

GitHub प्रोजेक्ट होमपेज से सीधे :

डैपर आपको IEnumerable में पास करने की अनुमति देता है और स्वचालित रूप से आपकी क्वेरी को पैरामीटर करेगा।

connection.Query<int>(
    @"select * 
      from (select 1 as Id union all select 2 union all select 3) as X 
      where Id in @Ids", 
    new { Ids = new int[] { 1, 2, 3 });

इसका अनुवाद किया जाएगा:

select * 
from (select 1 as Id union all select 2 union all select 3) as X 
where Id in (@Ids1, @Ids2, @Ids3)

// @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3

43

यदि आपका INक्लॉज़ MSSQL को संभालने के लिए बहुत बड़ा है, तो आप Dapper के साथ बहुत आसानी से TableValueParameter का उपयोग कर सकते हैं।

  1. MSSQL में अपना TVP प्रकार बनाएँ:

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
  2. DataTableटीवीपी के समान कॉलम (एस) के साथ बनाएं और इसे मानों के साथ आबाद करें

    var tvpTable = new DataTable();
    tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int)));
    // fill the data table however you wish
  3. INNER JOINTVP टेबल पर करने के लिए अपनी Dapper क्वेरी को संशोधित करें :

    var query = @"SELECT * FROM Providers P
        INNER JOIN @tvp t ON p.ProviderId = t.ProviderId";
  4. अपने Dapper क्वेरी कॉल में DataTable पास करें

    sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});

जब आप कई स्तंभों का एक सामूहिक अद्यतन करना चाहते हैं, तो यह काल्पनिक रूप से भी काम करता है - बस एक टीवीपी का निर्माण करें और टीवीपी में UPDATEएक आंतरिक जुड़ाव के साथ करें।


महान समाधान, हालांकि .Net कोर पर काम नहीं करता है, इस प्रश्न को देखें: stackoverflow.com/questions/41132350/… । इस पेज को भी देखें: github.com/StackExchange/Dapper/issues/603
pcdev

3
आप इसे बनाने ProviderIdपर भी विचार कर MyTVPसकते हैं PRIMARY KEY CLUSTERED, क्योंकि इससे हमारे लिए केवल एक प्रदर्शन समस्या हल हो गई है (हम जिन मूल्यों को पारित कर रहे हैं उनमें कोई डुप्लिकेट नहीं था)।
रिचर्डिसिमो

@Richardissimo क्या आप ऐसा करने का एक उदाहरण दिखा सकते हैं? मैं वाक्यविन्यास को सही प्रतीत नहीं कर सकता।
माइक कोल


14

संभवतः आईडी की सूची का उपयोग करके डैपर के साथ बड़ी संख्या में पंक्तियों को क्वेरी करने का सबसे तेज़ तरीका है। मैं आपसे वादा करता हूं कि यह लगभग किसी भी अन्य तरीके से तेज है, जिसके बारे में आप सोच सकते हैं (एक टीवीपी का उपयोग करने के संभावित अपवाद के रूप में, जो दूसरे उत्तर में दिया गया है, और जिसे मैंने परीक्षण नहीं किया है, लेकिन मुझे संदेह है कि आप धीमी हो सकते हैं क्योंकि आपको अभी भी आबाद करना है TVP)। यह डायपर की तुलना में तेजी से ग्रह हैIN और पंक्ति द्वारा इकाई फ्रेमवर्क पंक्ति की तुलना में तेजी से सिंटैक्स और ब्रह्मांडों का उपयोग कर रहा है । और यह वस्तुओं VALUESया UNION ALL SELECTवस्तुओं की सूची में पास होने से भी तेज है । इसे आसानी से एक बहु-स्तंभ कुंजी का उपयोग करने के लिए बढ़ाया जा सकता है, बस अतिरिक्त स्तंभों को DataTable, अस्थायी तालिका और सम्मिलित स्थितियों में जोड़ सकते हैं।

public IReadOnlyCollection<Item> GetItemsByItemIds(IEnumerable<int> items) {
   var itemList = new HashSet(items);
   if (itemList.Count == 0) { return Enumerable.Empty<Item>().ToList().AsReadOnly(); }

   var itemDataTable = new DataTable();
   itemDataTable.Columns.Add("ItemId", typeof(int));
   itemList.ForEach(itemid => itemDataTable.Rows.Add(itemid));

   using (SqlConnection conn = GetConnection()) // however you get a connection
   using (var transaction = conn.BeginTransaction()) {
      conn.Execute(
         "CREATE TABLE #Items (ItemId int NOT NULL PRIMARY KEY CLUSTERED);",
         transaction: transaction
      );

      new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction) {
         DestinationTableName = "#Items",
         BulkCopyTimeout = 3600 // ridiculously large
      }
         .WriteToServer(itemDataTable);
      var result = conn
         .Query<Item>(@"
            SELECT i.ItemId, i.ItemName
            FROM #Items x INNER JOIN dbo.Items i ON x.ItemId = i.ItemId
            DROP TABLE #Items;",
            transaction: transaction,
            commandTimeout: 3600
         )
         .ToList()
         .AsReadOnly();
      transaction.Rollback(); // Or commit if you like
      return result;
   }
}

ध्यान रखें कि आपको बल्क इंसर्ट के बारे में थोड़ा जानने की जरूरत है। फायरिंग ट्रिगर (डिफ़ॉल्ट नहीं है) के बारे में विकल्प हैं, बाधाओं का सम्मान करते हुए, टेबल को लॉक करना, समवर्ती आवेषण की अनुमति देना, और इसी तरह।


हां मैं Ids के साथ एक अस्थायी तालिका बनाने और फिर उस तालिका में आंतरिक रूप से जुड़ने के आपके सामान्य विचार से सहमत हूं। हमने आंतरिक रूप से ऐसा किया है और इसने क्वेरी प्रदर्शन में काफी सुधार किया है। मुझे यकीन नहीं है कि मैं किसी भी चीज़ के लिए डेटाटेबल क्लास का उपयोग करूंगा लेकिन आपका समाधान पूरी तरह से मान्य है। यह बहुत तेज तरीका है।
मार्को

DataTableथोक डालने के लिए आवश्यक है। कैसे करते हैं आप अस्थायी तालिका में 50,000 मूल्यों डालने?
एरिक

1
1000 के विखंडू में अगर मुझे सीमा ठीक से याद है? वैसे भी मुझे नहीं पता था कि आप डेटाटेबल के साथ सीमा को दरकिनार कर सकते हैं इसलिए मैंने आज कुछ नया सीखा ...
मार्को

1
जब आप टेबल वैल्यू पैरामीटर का उपयोग कर सकते हैं तो जाने के लिए यह एक हास्यास्पद राशि है। डैपर एक टीवीटी के रूप में एक डेटाटेबल को पारित करने में सफाई का समर्थन करता है, जो आपको एक टेम्प टेबल के निर्माण और विनाश के साथ-साथ बल्कोपी के माध्यम से उस टेम्प टेबल को पॉपुलेट करने की अनुमति देता है। हम उन मामलों में नियमित रूप से टीवीपी आधारित समाधान का उपयोग करते हैं जहां IN खंड के लिए मापदंडों की संख्या बहुत अधिक होगी।
श्री टी।

3
यह काम की एक हास्यास्पद राशि नहीं है, खासकर अगर कोई इसे सहायक वर्ग या विस्तार पद्धति के साथ थोड़ा दूर करता है।
एरिक

11

यह भी सुनिश्चित करें कि आप अपनी क्वेरी स्ट्रिंग के चारों ओर कोष्ठक को लपेटते नहीं हैं जैसे:

SELECT Name from [USER] WHERE [UserId] in (@ids)

कोष्ठक को हटाकर तय किए गए डेपर 1.50.2 का उपयोग करके मेरे पास SQL ​​सिंटैक्स त्रुटि का कारण था

SELECT Name from [USER] WHERE [UserId] in @ids

7

यह है जरूरी नहीं जोड़ने के लिए ()कहां खंड में के रूप में हम एक नियमित रूप से एसक्यूएल में करते हैं। क्योंकि डॅपर हमारे लिए अपने आप होता है। यहाँ है syntax: -

const string SQL = "SELECT IntegerColumn, StringColumn FROM SomeTable WHERE IntegerColumn IN @listOfIntegers";

var conditions = new { listOfIntegers };

var results = connection.Query(SQL, conditions);

6

पोस्टग्रेज के लिए उदाहरण:

string sql = "SELECT * FROM SomeTable WHERE id = ANY(@ids)"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});

3

मेरे मामले में मैंने इसका उपयोग किया है:

var query = "select * from table where Id IN @Ids";
var result = conn.Query<MyEntity>(query, new { Ids = ids });

दूसरी पंक्ति में मेरा वैरिएबल "आईडी" तार का एक IEnumerable है, यह भी कि वे मुझे लगता है कि पूर्णांक हो सकते हैं।


List<string>?
किकेनेट

2

मेरे अनुभव में, इससे निपटने का सबसे अनुकूल तरीका एक फ़ंक्शन है जो एक स्ट्रिंग को मूल्यों की तालिका में परिवर्तित करता है।

वेब पर कई अलग-अलग कार्य उपलब्ध हैं, यदि आपको एसक्यूएल का स्वाद पसंद है तो आपको आसानी से मिल जाएगा।

फिर आप कर सकते हैं ...

SELECT * FROM table WHERE id IN (SELECT id FROM split(@list_of_ids))

या

SELECT * FROM table INNER JOIN (SELECT id FROM split(@list_of_ids)) AS list ON list.id = table.id

(या इसी के समान)

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