अधिकतम या डिफ़ॉल्ट?


176

LINQ क्वेरी से अधिकतम मान प्राप्त करने का सबसे अच्छा तरीका क्या है जो बिना पंक्तियों के वापस आ सकता है? अगर मैं बस करूँ

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter).Max

जब क्वेरी बिना पंक्तियों के वापस आती है तो मुझे एक त्रुटि मिलती है। मैं कर सकता था

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter _
         Order By MyCounter Descending).FirstOrDefault

लेकिन वह इस तरह के एक साधारण अनुरोध के लिए थोड़ा मोटा महसूस करता है। क्या मुझे इसे करने का एक बेहतर तरीका याद आ रहा है?

अद्यतन: यहाँ पीछे की कहानी है: मैं एक बच्चे की मेज (विरासत प्रणाली से अगली पात्रता काउंटर को पुनः प्राप्त करने की कोशिश कर रहा हूं, मुझे शुरू मत करो ...)। प्रत्येक रोगी के लिए पहली पात्रता पंक्ति हमेशा 1 है, दूसरी 2 है, आदि (जाहिर है कि यह बच्चे की मेज की प्राथमिक कुंजी नहीं है)। इसलिए, मैं एक मरीज के लिए अधिकतम मौजूदा काउंटर मान का चयन कर रहा हूं, और फिर एक नई पंक्ति बनाने के लिए इसमें 1 जोड़ रहा हूं। जब कोई मौजूदा बाल मूल्य नहीं हैं, तो मुझे 0 वापस करने के लिए क्वेरी की आवश्यकता है (इसलिए 1 को जोड़ने से मुझे 1 का काउंटर मान मिलेगा)। ध्यान दें कि मैं बच्चे की पंक्तियों की कच्ची गिनती पर भरोसा नहीं करना चाहता, यदि विरासत ऐप काउंटर मूल्यों (संभव) में अंतराल का परिचय देता है। प्रश्न को भी सामान्य बनाने की कोशिश करने के लिए मेरा बुरा।

जवाबों:


206

चूंकि DefaultIfEmptyएसक्यूएल को लिनक्यू में लागू नहीं किया गया है, मैंने उस त्रुटि पर एक खोज की और इसे एक आकर्षक लेख मिला, जो समग्र कार्यों में शून्य सेट से संबंधित है। मुझे जो कुछ भी मिला है उसे संक्षेप में बताने के लिए, आप अपने चयन के भीतर एक अशक्त के लिए कास्टिंग करके इस सीमा के आसपास प्राप्त कर सकते हैं। मेरा VB थोड़ा कठोर है, लेकिन मुझे लगता है कि यह कुछ इस तरह होगा:

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select CType(y.MyCounter, Integer?)).Max

या C # में:

var x = (from y in context.MyTable
         where y.MyField == value
         select (int?)y.MyCounter).Max();

1
VB को ठीक करने के लिए, सिलेक्ट "Select CType (y.MyCounter, Integer?)" होगा। मुझे अपने उद्देश्यों के लिए कुछ भी नहीं 0 में बदलने के लिए एक मूल जांच करनी है, लेकिन मुझे बिना किसी अपवाद के परिणाम प्राप्त करना पसंद है।
gfrizzle

2
DefaultIfEmpty के दो अधिभार में से एक LINQ से SQL में समर्थित है - वह जो पैरामीटर नहीं लेता है।
डेमियन जी

संभवतः यह जानकारी पुरानी है, क्योंकि मैंने LINQ में DefaultIfEmpty के दोनों रूपों को SQL में सफलतापूर्वक परीक्षण किया है
Neil

3
@ नील: कृपया एक उत्तर दें। DefaultIfEmpty मेरे लिए काम नहीं करता है: मैं Maxएक चाहता हूँ DateTimeMax(x => (DateTime?)x.TimeStamp)अभी भी एक ही रास्ता ..
duedl0r

1
हालाँकि DefaultIfEmpty अब LINQ to SQL में लागू हो गया है, लेकिन यह उत्तर बेहतर IMO बना रहता है, क्योंकि SQL कथन 'SELECT MyCounter' में DefaultIfEmpty परिणामों का उपयोग करते हुए, जो हर मूल्य के लिए एक पंक्ति लौटाता है , जबकि MAX (MyCounter) में यह उत्तर परिणाम देता है जो रिटर्न करता है एकल, सार पंक्ति। (EntityFrameworkCore 2.1.3 में परीक्षण किया गया।)
कार्ल शरमन

107

मुझे बस एक समान समस्या थी, लेकिन मैं क्वेरी सिंटैक्स के बजाय एक सूची पर LINQ एक्सटेंशन विधियों का उपयोग कर रहा था। एक अशक्त चाल के लिए कास्टिंग वहाँ भी काम करता है:

int max = list.Max(i => (int?)i.MyCounter) ?? 0;

48

के लिए एक मामले की तरह लगता है DefaultIfEmpty(अनुत्तरित कोड निम्न):

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter).DefaultIfEmpty.Max

मैं DefaultIfEmpty से परिचित नहीं हूँ, लेकिन मुझे "सिंट के रूप में निष्पादन के लिए 'OptionalValue' नोड को प्रारूपित नहीं कर सका" जब वाक्य रचना का उपयोग किया जाता है। मैंने एक डिफ़ॉल्ट मान (शून्य) प्रदान करने का भी प्रयास किया, लेकिन यह ऐसा नहीं था।
gfrizzle

आह। लगता है कि DefaultIfEmpty SQL में LINQ में समर्थित नहीं है। आप .TLList के साथ पहले सूची में शामिल होकर प्राप्त कर सकते हैं लेकिन यह एक महत्वपूर्ण प्रदर्शन है।
जैकब प्रोफिट

3
धन्यवाद, यह वही है जिसकी मुझे तलाश थी। विस्तार के तरीकों का उपयोग करना:var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
जानी

35

आप जो पूछ रहे हैं, उसके बारे में सोचें!

{1, 2, 3, -1, -2, -3} का अधिकतम 3. स्पष्ट रूप से 3. {2} का अधिकतम है जाहिर है 2. लेकिन खाली सेट {} का अधिकतम क्या है? जाहिर है कि यह एक व्यर्थ प्रश्न है। खाली सेट की अधिकतम सीमा को परिभाषित नहीं किया गया है। उत्तर पाने का प्रयास करना गणितीय त्रुटि है। किसी भी सेट का अधिकतम उस सेट में एक तत्व होना चाहिए। खाली सेट में कोई तत्व नहीं है, इसलिए यह दावा करना कि कुछ विशेष संख्या उस सेट में अधिकतम है उस सेट में गणितीय विरोधाभास है।

जिस प्रकार प्रोग्रामर को शून्य से भाग देने के लिए कहने पर कंप्यूटर के लिए एक अपवाद को फेंकना सही व्यवहार है, इसलिए कंप्यूटर के लिए एक अपवाद को फेंकने के लिए सही व्यवहार है जब प्रोग्रामर इसे खाली सेट का अधिकतम लेने के लिए कहता है। शून्य से डिवीजन, खाली सेट का अधिकतम हिस्सा लेना, स्पैक्लेरोरके को चकाचौंध करना, और नेवरलैंड के लिए यूनिकॉर्न की सवारी करना सभी अर्थहीन, असंभव, अपरिभाषित हैं।

अब, ऐसा क्या है जो आप वास्तव में करना चाहते हैं?


अच्छा बिंदु - मैं उन विवरणों के साथ शीघ्र ही अपना प्रश्न अपडेट करूंगा। यह कहने के लिए पर्याप्त है कि मुझे पता है कि मैं 0 चाहता हूं जब चयन करने के लिए कोई रिकॉर्ड नहीं हैं, जो निश्चित रूप से अंतिम समाधान पर प्रभाव डालता है।
गृहत्यागी

17
मैं अक्सर नेवरलैंड के लिए अपने गेंडा उड़ाने का प्रयास करता हूं, और मैं आपके सुझाव पर अमल करता हूं कि मेरे प्रयास निरर्थक और अपरिभाषित हैं।
क्रिस शट्स

2
मुझे नहीं लगता कि यह तर्क सही है। यह स्पष्ट linq-to-sql है, और sql Max में शून्य पंक्तियों को शून्य के रूप में परिभाषित किया गया है, नहीं?
duedl0r

4
Linq को आम तौर पर समान परिणामों का उत्पादन करना चाहिए कि क्या क्वेरी को ऑब्जेक्ट पर मेमोरी में निष्पादित किया जाता है या क्या क्वेरी को पंक्तियों पर डेटाबेस में निष्पादित किया जाता है। Linq क्वेरी Linq क्वेरीज़ हैं, और जो एडाप्टर उपयोग में है, उसकी परवाह किए बिना विश्वासपूर्वक निष्पादित किया जाना चाहिए।
yfeldblum

1
जबकि मैं इस सिद्धांत में सहमत हूं कि Linq परिणाम समान होना चाहिए चाहे वह मेमोरी में निष्पादित हो या sql में, जब आप वास्तव में थोड़ा गहरा खुदाई करते हैं, तो आपको पता चलता है कि यह हमेशा ऐसा क्यों नहीं हो सकता है। जटिल अभिव्यक्ति अनुवाद का उपयोग करके Linq अभिव्यक्तियों को sql में अनुवादित किया जाता है। यह एक-से-एक सरल अनुवाद नहीं है। एक अंतर शून्य की स्थिति है। C # "null == null" में सत्य है। SQL में, "null == null" मैच बाहरी जॉइन के लिए शामिल होते हैं लेकिन इनर जॉइन के लिए नहीं। हालाँकि, आंतरिक जोड़ लगभग हमेशा वही होते हैं जो आप चाहते हैं ताकि वे डिफ़ॉल्ट हों। यह व्यवहार में संभावित अंतर का कारण बनता है।
कर्टिस येलोप

25

आप हमेशा Double.MinValueअनुक्रम में जोड़ सकते हैं । यह सुनिश्चित करेगा कि कम से कम एक तत्व हो और Maxयह तभी लौटाएगा जब यह वास्तव में न्यूनतम हो। निर्धारित करने के लिए कौन-सा विकल्प और अधिक कुशल है ( Concat, FirstOrDefaultया Take(1)), तो आपको पर्याप्त बेंच मार्किंग प्रदर्शन करना चाहिए।

double x = context.MyTable
    .Where(y => y.MyField == value)
    .Select(y => y.MyCounter)
    .Concat(new double[]{Double.MinValue})
    .Max();

10
int max = list.Any() ? list.Max(i => i.MyCounter) : 0;

यदि सूची में कोई तत्व है (अर्थात खाली नहीं है), तो यह MyCounter फ़ील्ड का अधिकतम लेगा, अन्यथा 0 वापस आ जाएगा।


3
क्या इससे 2 प्रश्न नहीं चलेंगे?
andreapier

10

.Net 3.5 के बाद से आप DefaultIfEmpty () डिफॉल्ट मान को एक तर्क के रूप में उपयोग कर सकते हैं। अनुसरण के तरीकों में से कुछ:

int max = (from e in context.Table where e.Year == year select e.RecordNumber).DefaultIfEmpty(0).Max();
DateTime maxDate = (from e in context.Table where e.Year == year select e.StartDate ?? DateTime.MinValue).DefaultIfEmpty(DateTime.MinValue).Max();

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

परिणामी चयन इतना सुरुचिपूर्ण नहीं होगा लेकिन यह स्वीकार्य है।

आशा करता हूँ की ये काम करेगा।


7

मुझे लगता है कि मुद्दा यह है कि आप क्या करना चाहते हैं जब क्वेरी का कोई परिणाम नहीं है। यदि यह एक असाधारण मामला है तो मैं क्वेरी को एक कोशिश / कैच ब्लॉक में लपेटूंगा और उस अपवाद को संभालूंगा जो मानक क्वेरी उत्पन्न करता है। यदि क्वेरी का कोई परिणाम नहीं आना ठीक है, तो आपको यह पता लगाना होगा कि आप उस मामले में क्या परिणाम चाहते हैं। यह हो सकता है कि @ डेविड का जवाब (या ऐसा ही कुछ काम करेगा)। यही है, अगर मैक्स हमेशा सकारात्मक होगा, तो यह सूची में एक ज्ञात "खराब" मान डालने के लिए पर्याप्त हो सकता है जो केवल तभी चुना जाएगा जब कोई परिणाम नहीं होगा। आम तौर पर, मैं एक ऐसी क्वेरी की अपेक्षा करता हूं जो काम करने के लिए कुछ डेटा प्राप्त करने के लिए अधिकतम पुनर्प्राप्त कर रही हो और मैं कोशिश / कैच मार्ग पर जाऊंगा अन्यथा आप हमेशा यह जांचने के लिए मजबूर होते हैं कि आपके द्वारा प्राप्त मूल्य सही है या नहीं। मैं'

Try
   Dim x = (From y In context.MyTable _
            Where y.MyField = value _
            Select y.MyCounter).Max
   ... continue working with x ...
Catch ex As SqlException
       ... do error processing ...
End Try

मेरे मामले में, कोई पंक्तियाँ नहीं होने से अधिक बार होता है (विरासत प्रणाली, रोगी की पिछली पात्रता, ब्ला ब्ला ब्ला नहीं हो सकती है)। यदि यह अधिक असाधारण मामला होता, तो मैं शायद इस मार्ग पर जाता (हालांकि मैं अभी भी, बेहतर नहीं देख रहा था)।
शाम

6

एक और संभावना समूहन की होगी, आप इसे कच्चे SQL में कैसे देख सकते हैं:

from y in context.MyTable
group y.MyCounter by y.MyField into GrpByMyField
where GrpByMyField.Key == value
select GrpByMyField.Max()

केवल एक चीज है (LINQPad में फिर से परीक्षण) VB LINQ स्वाद पर स्विच करना समूहन खंड पर वाक्यविन्यास त्रुटियां देता है। मुझे यकीन है कि वैचारिक समकक्ष को ढूंढना काफी आसान है, मुझे नहीं पता कि वीबी में इसे कैसे प्रतिबिंबित किया जाए।

उत्पन्न SQL कुछ लाइनों के साथ होगा:

SELECT [t1].[MaxValue]
FROM (
    SELECT MAX([t0].[MyCounter) AS [MaxValue], [t0].[MyField]
    FROM [MyTable] AS [t0]
    GROUP BY [t0].[MyField]
    ) AS [t1]
WHERE [t1].[MyField] = @p0

नेस्टेड SELECT icky दिखता है, जैसे क्वेरी निष्पादन सभी पंक्तियों को फिर से प्राप्त करेगा, फिर से प्राप्त सेट में से एक का चयन करें ... प्रश्न यह है कि SQL सर्वर क्वेरी को आंतरिक सेलेक्ट में लागू करने के लिए तुलनीय है या नहीं। मैं अब उस में देख रहा हूँ ...

मैं SQL सर्वर में निष्पादन योजनाओं की व्याख्या करने के लिए अच्छी तरह से वाकिफ नहीं हूं, लेकिन ऐसा लगता है कि जब WHERE क्लॉज बाहरी चयन पर होता है, तो उस चरण में जिसके परिणामस्वरूप वास्तविक पंक्तियों की संख्या तालिका में सभी पंक्तियां होती हैं, बनाम केवल मिलान पंक्तियां जब आंतरिक खंड पर WHERE क्लॉज है। उस ने कहा, ऐसा लगता है कि केवल 1% लागत को निम्नलिखित चरण में स्थानांतरित कर दिया जाता है जब सभी पंक्तियों को माना जाता है, और किसी भी तरह से केवल एक पंक्ति कभी SQL सर्वर से वापस आती है, इसलिए शायद यह चीजों की भव्य योजना में अंतर से बड़ा नहीं है ।


6

देर से ही सही, लेकिन मुझे भी यही चिंता थी ...

मूल कोड से अपने कोड को रीफ़्रेश करते हुए, आप चाहते हैं कि S द्वारा निर्धारित सेट की अधिकतम हो

(From y In context.MyTable _
 Where y.MyField = value _
 Select y.MyCounter)

आपकी अंतिम टिप्पणी को ध्यान में रखते हुए

यह कहते हुए

मैं आपकी समस्या को फिर से बता सकता हूं: आप अधिकतम {0 + S} चाहते हैं। और ऐसा लगता है कि समवर्ती के साथ प्रस्तावित समाधान शब्दशः सही है :-)

var max = new[]{0}
          .Concat((From y In context.MyTable _
                   Where y.MyField = value _
                   Select y.MyCounter))
          .Max();

3

क्यों नहीं कुछ और अधिक प्रत्यक्ष जैसे:

Dim x = context.MyTable.Max(Function(DataItem) DataItem.MyField = Value)

1

एक दिलचस्प अंतर जो ध्यान देने योग्य लगता है वह यह है कि FirstOrDefault और Take (1) एक ही SQL उत्पन्न करते हैं (LINQPad, वैसे भी) के अनुसार, FirstOrDefault एक मान देता है - डिफ़ॉल्ट - जब कोई मिलान पंक्तियाँ नहीं होती हैं और (1) रिटर्न मिलता है कोई परिणाम नहीं ... कम से कम LINQPad में।


1

बस हर किसी को यह बताने का मौका देना चाहिए कि Linq का उपयोग Entities के लिए ऊपर दिए गए तरीके काम नहीं करेंगे ...

अगर आप कुछ ऐसा करने की कोशिश करते हैं

var max = new[]{0}
      .Concat((From y In context.MyTable _
               Where y.MyField = value _
               Select y.MyCounter))
      .Max();

यह एक अपवाद फेंक देगा:

System.NotSupportedException: LINQ अभिव्यक्ति नोड प्रकार 'NewArrayInit' LINQ में संस्थाओं को समर्थित नहीं है ..

मैं सिर्फ करने का सुझाव दूंगा

(From y In context.MyTable _
                   Where y.MyField = value _
                   Select y.MyCounter))
          .OrderByDescending(x=>x).FirstOrDefault());

और FirstOrDefaultअगर आपकी सूची खाली है तो 0 वापस आ जाएगी।


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


1

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

    public static TResult MaxOrDefault<TSource, TResult>(this 
    IQueryable<TSource> source, Expression<Func<TSource, TResult?>> selector,
    TResult defaultValue = default (TResult)) where TResult : struct
    {
        return source.Max(selector) ?? defaultValue;
    }

0

SQL के लिए Entity Framework और Linq के लिए हम इसे एक एक्सटेंशन मेथड को परिभाषित करके प्राप्त कर सकते हैं जो एक Expressionउत्तीर्ण विधि को संशोधित करता है IQueryable<T>.Max(...):

static class Extensions
{
    public static TResult MaxOrDefault<T, TResult>(this IQueryable<T> source, 
                                                   Expression<Func<T, TResult>> selector)
        where TResult : struct
    {
        UnaryExpression castedBody = Expression.Convert(selector.Body, typeof(TResult?));
        Expression<Func<T, TResult?>> lambda = Expression.Lambda<Func<T,TResult?>>(castedBody, selector.Parameters);
        return source.Max(lambda) ?? default(TResult);
    }
}

उपयोग:

int maxId = dbContextInstance.Employees.MaxOrDefault(employee => employee.Id);
// maxId is equal to 0 if there is no records in Employees table

उत्पन्न क्वेरी समान है, यह IQueryable<T>.Max(...)विधि के लिए एक सामान्य कॉल की तरह काम करता है , लेकिन अगर कोई रिकॉर्ड नहीं है तो यह एक छूट फेंकने के बजाय टाइप T का डिफ़ॉल्ट मान लौटाता है


-1

मुझे बस इसी तरह की समस्या थी, मेरे यूनिट परीक्षण मैक्स () का उपयोग करके पारित हुए लेकिन लाइव डेटाबेस के खिलाफ चलने पर विफल रहे।

मेरा समाधान क्वेरी को प्रदर्शन किए जाने से अलग करना था, उन्हें एक क्वेरी में शामिल नहीं करना था।
मुझे एक लाइव वातावरण में निष्पादित करते समय लिनक-ऑब्जेक्ट्स (लिनक-ऑब्जेक्ट्स मैक्स में) (नल के साथ काम करता है) और लिनक-एसक्यूएल का उपयोग करके यूनिट परीक्षणों में काम करने के लिए एक समाधान की आवश्यकता थी।

(मैं अपने परीक्षणों में चयन () का मजाक उड़ाता हूं)

var requiredDataQuery = _dataRepo.Select(x => new { x.NullableDate1, .NullableDate2 }); 
var requiredData.ToList();
var maxDate1 = dates.Max(x => x.NullableDate1);
var maxDate2 = dates.Max(x => x.NullableDate2);

कम सक्षम? शायद।

क्या मुझे परवाह है, जब तक कि अगली बार मेरा ऐप गिर न जाए? नहीं।

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