LINQ JOIN को WHERE के साथ लिंक करने में इतना तेज़ क्यों है?


99

मैंने हाल ही में VS 2010 में अपग्रेड किया है और मैं LINQ से डेटासेट के आसपास खेल रहा हूं। मेरे पास प्राधिकरण के लिए एक मजबूत टाइप की गई डेटासेट है जो एक ASP.NET WebApplication के HttpCache में है।

इसलिए मैं यह जानना चाहता था कि वास्तव में यह जांचने का सबसे तेज़ तरीका है कि कोई उपयोगकर्ता कुछ करने के लिए अधिकृत है या नहीं। यहाँ मेरा डेटामोडेल और कुछ अन्य informations है अगर कोई दिलचस्पी रखता है।

मैंने 3 तरीके जाँचे हैं:

  1. प्रत्यक्ष डेटाबेस
  2. LINQ क्वेरी "जॉइन" के रूप में कहां की शर्तों के साथ - सिंटैक्स
  3. LINQ क्वेरी Join - सिंटैक्स के साथ

ये प्रत्येक फ़ंक्शन पर 1000 कॉल के साथ परिणाम हैं:

1.Iteration:

  1. 4,2841519 सेकंड।
  2. 115,7796925 सेकंड।
  3. 2,024749 सेकंड।

2.Iteration:

  1. 3,1954857 सेकंड।
  2. 84,97047 सेकंड।
  3. 1,5783397 सेकंड।

3.Iteration:

  1. 2,7922143 सेकंड।
  2. 97,8713267 सेकंड।
  3. 1,8432163 सेकंड।

औसत:

  1. डेटाबेस: 3,4239506333 सेकंड।
  2. कहां: 99,5404964 सेकंड।
  3. जुड़ें: 1,815435 सेकंड।

जहां-सिंटैक्स की तुलना में जॉइन-वर्जन इतना तेज क्यों है जो इसे बेकार बनाता है हालांकि एक LINQ नौसिखिया के रूप में यह सबसे सुपाच्य लगता है। या मैं अपने प्रश्नों में कुछ याद किया है?

यहाँ LINQ प्रश्न हैं, मैं डेटाबेस को छोड़ देता हूँ:

कहां :

Public Function hasAccessDS_Where(ByVal accessRule As String) As Boolean
    Dim userID As Guid = DirectCast(Membership.GetUser.ProviderUserKey, Guid)
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule, _
                roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule, _
                role In Authorization.dsAuth.aspnet_Roles, _
                userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                Where accRule.idAccessRule = roleAccRule.fiAccessRule _
                And roleAccRule.fiRole = role.RoleId _
                And userRole.RoleId = role.RoleId _
                And userRole.UserId = userID And accRule.RuleName.Contains(accessRule)
                Select accRule.idAccessRule
    Return query.Any
End Function

में शामिल हों:

Public Function hasAccessDS_Join(ByVal accessRule As String) As Boolean
    Dim userID As Guid = DirectCast(Membership.GetUser.ProviderUserKey, Guid)
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule _
                Join roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule _
                On accRule.idAccessRule Equals roleAccRule.fiAccessRule _
                Join role In Authorization.dsAuth.aspnet_Roles _
                On role.RoleId Equals roleAccRule.fiRole _
                Join userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                On userRole.RoleId Equals role.RoleId _
                Where userRole.UserId = userID And accRule.RuleName.Contains(accessRule)
                Select accRule.idAccessRule
    Return query.Any
End Function

पहले ही, आपका बहुत धन्यवाद।


संपादित करें : अधिक सार्थक पूर्णता-मूल्यों को प्राप्त करने के लिए दोनों प्रश्नों पर कुछ सुधारों के बाद, JOIN का लाभ पहले की तुलना में कई गुना अधिक है:

शामिल हों :

Public Overloads Shared Function hasAccessDS_Join(ByVal userID As Guid, ByVal idAccessRule As Int32) As Boolean
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule _
                   Join roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule _
                   On accRule.idAccessRule Equals roleAccRule.fiAccessRule _
                   Join role In Authorization.dsAuth.aspnet_Roles _
                   On role.RoleId Equals roleAccRule.fiRole _
                   Join userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                   On userRole.RoleId Equals role.RoleId _
                   Where accRule.idAccessRule = idAccessRule And userRole.UserId = userID
             Select role.RoleId
    Return query.Any
End Function

कहां :

Public Overloads Shared Function hasAccessDS_Where(ByVal userID As Guid, ByVal idAccessRule As Int32) As Boolean
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule, _
           roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule, _
           role In Authorization.dsAuth.aspnet_Roles, _
           userRole In Authorization.dsAuth.aspnet_UsersInRoles _
           Where accRule.idAccessRule = roleAccRule.fiAccessRule _
           And roleAccRule.fiRole = role.RoleId _
           And userRole.RoleId = role.RoleId _
           And accRule.idAccessRule = idAccessRule And userRole.UserId = userID
           Select role.RoleId
    Return query.Any
End Function

1000 कॉल के लिए परिणाम (तेज कंप्यूटर पर)

  1. सम्मिलित हों | 2. कहाँ

1.Iteration:

  1. 0,0713669 सेकंड।
  2. 12,7395299 सेकंड।

2.Iteration:

  1. 0,0492458 सेकंड।
  2. 12,3885925 सेकंड।

3.Iteration:

  1. 0,0501982 सेकंड।
  2. 13,3474216 सेकंड।

औसत:

  1. शामिल हों: 0,0569367 सेकंड।
  2. कहां: 12,8251813 सेकंड।

ज्वाइन करना 225 गुना तेज है

निष्कर्ष: संबंधों को निर्दिष्ट करने के लिए जहां भी संभव हो और जोइन का उपयोग करने से बचें (निश्चित रूप से डेटासेट और Linq-To-Objectsसामान्य रूप से LINQ में)।


दूसरों के लिए जो इसे पढ़ते हैं और LinqToSQL का उपयोग कर रहे हैं और सोचते हैं कि आपके सभी WHERE को JOIN में बदलना अच्छा हो सकता है, कृपया सुनिश्चित करें कि आप Thomas Levesque द्वारा टिप्पणी पढ़ें जहां वह कहते हैं कि "Linq to SQL का उपयोग करते समय इस तरह का अनुकूलन होता है" Linq to Entities, क्योंकि उत्पन्न SQL क्वेरी को DBMS द्वारा जुड़ने के रूप में माना जाता है। लेकिन उस स्थिति में आप Linq का उपयोग DataSet में कर रहे हैं, SQL में कोई अनुवाद नहीं है "। दूसरे शब्दों में, जब आप linqtosql का उपयोग कर रहे हैं, तो इसमें शामिल होने के लिए कुछ भी बदलने से परेशान न हों।
जॉन जूल

@ जॉन: यह किसी भी तरह का उपयोग करने के लिए चोट नहीं करता है Join, आप एक ऑप्टिमाइज़र पर भरोसा क्यों करते हैं यदि आप शुरुआत से ओपिमाइज्ड कोड लिख सकते हैं? यह आपके इरादों को भी स्पष्ट करता है। तो वही कारण जो आपको sql में JOIN करना चाहिए
टिम श्मेल्टर

क्या मैं यह मानने के लिए सही हूं कि यह EntityFramework के मामले में नहीं होगा?
माफिया जूल

जवाबों:


76
  1. आपका पहला दृष्टिकोण (DB में SQL क्वेरी) काफी कुशल है क्योंकि DB जानता है कि किसी कार्य को कैसे करना है। लेकिन यह वास्तव में अन्य दृष्टिकोणों के साथ तुलना करने के लिए समझ में नहीं आता है, क्योंकि वे सीधे मेमोरी में काम करते हैं (Linq to DataSet)

  2. कई तालिकाओं और एक Whereशर्त के साथ क्वेरी वास्तव में सभी तालिकाओं का एक कार्टेशियन उत्पाद करता है , फिर उन पंक्तियों को फ़िल्टर करता है जो स्थिति को संतुष्ट करते हैं। इसका मतलब है कि Whereप्रत्येक पंक्ति (n1 * n2 * n3 * n4) के संयोजन के लिए स्थिति का मूल्यांकन किया जाता है

  3. Joinऑपरेटर, पहले टेबल से पंक्तियों लेता है तो दूसरी मेज से मिलान कुंजी है, तो केवल तीसरी मेज से मिलान कुंजी के साथ पंक्तियों, और इतने पर के साथ ही पंक्तियों लेता है। यह बहुत अधिक कुशल है, क्योंकि इसे कई ऑपरेशन करने की आवश्यकता नहीं है


4
पृष्ठभूमि को स्पष्ट करने के लिए धन्यवाद। डीबी दृष्टिकोण वास्तव में इस प्रश्न का हिस्सा नहीं था, लेकिन मेरे लिए यह देखना दिलचस्प था कि क्या स्मृति दृष्टिकोण वास्तव में तेज है। मुझे लगता है कि .net whereकिसी तरह dbms के रूप में -query का अनुकूलन होगा । वास्तव में (पिछले संपादन) की JOINतुलना में 225 गुना तेज था WHERE
टिम शाल्टर

19

यह Joinबहुत तेज़ है, क्योंकि विधि जानती है कि संबंधित संयोजनों के परिणाम को कम करने के लिए तालिकाओं को कैसे संयोजित किया जाए। जब आप Whereसंबंध को निर्दिष्ट करने के लिए उपयोग करते हैं, तो इसे हर संभव संयोजन बनाना पड़ता है, और फिर यह देखने के लिए कि कौन से संयोजन प्रासंगिक हैं, इस शर्त का परीक्षण करें।

Joinविधि एक सूचकांक के रूप में उपयोग करने के लिए एक हैश तालिका quicky दो तालिकाओं एक साथ ज़िप करने, जबकि निर्धारित कर सकते हैं Whereसभी संयोजनों के बाद विधि रन पहले से ही बनाई गई हैं, तो यह किसी भी चाल उपयोग नहीं कर सकते पहले से संयोजन कम करने के लिए।


धन्यवाद। क्या dbms की तरह संकलक / रनटाइम से कोई अंतर्निहित अनुकूलन नहीं हैं? यह देखना असंभव नहीं होना चाहिए कि वास्तव में संबंध कहां है।
टिम श्मेल्टर

1
एक अच्छा RDBMS को वास्तव में यह पता होना चाहिए कि WHERE की स्थिति दो UNIQUE कॉलम में समानता के लिए एक परीक्षण है और इसे एक JOIN के रूप में माना जाता है।
साइमन रिक्टर

6
@ टिम स्केलेटर, जब आप SQL या Linq से Entities को Linq का उपयोग करते हैं, तो ऐसा अनुकूलन होता है, क्योंकि उत्पन्न SQL क्वेरी को DBMS द्वारा जुड़ने के रूप में माना जाता है। लेकिन उस मामले में आप DataSet में Linq का उपयोग कर रहे हैं, SQL का कोई अनुवाद नहीं है
थॉमस लेवेस्क

@ समय: डेटासेट में LINQ वास्तव में LINQ का उपयोग वस्तुओं के लिए करता है। नतीजतन, सच्चा जुड़ाव केवल joinकीवर्ड के साथ कब्जा किया जा सकता है , क्योंकि निष्पादन योजना के अनुरूप कुछ भी बनाने के लिए क्वेरी का कोई रनटाइम विश्लेषण नहीं है। आप यह भी देखेंगे कि LINQ- आधारित जॉइन केवल सिंगल-कॉलम इक्विजेंस को समायोजित कर सकते हैं।
एडम रॉबिन्सन

2
@ अदम, यह बिल्कुल सच नहीं है: आप कई प्रकारों के साथ समान कार्य कर सकते हैं, अनाम प्रकारों का उपयोग करके:... on new { f1.Key1, f1.Key2 } equals new { f2.Key1, f2.Key2 }
थॉमस लेवेस्क

7

क्या आप वास्तव में पता करने की जरूरत है कि sql है कि दो बयानों के लिए बनाया गया था। इसे प्राप्त करने के कुछ तरीके हैं लेकिन सबसे सरल है LinqPad का उपयोग करना। क्वेरी परिणामों के ठीक ऊपर कई बटन हैं जो कि sql में बदल जाएंगे। जो आपको किसी और चीज की तुलना में बहुत अधिक जानकारी देगा।

बढ़िया जानकारी हालांकि आपने वहाँ साझा की है।


1
LinqPad-hint के लिए धन्यवाद। वास्तव में मेरी दो क्वेरीज़ मेमोरी क्वैश्चंस में डेटासैट हैं, इसलिए मैं मानता हूं कि कोई SQL जनरेट नहीं हुआ है। आम तौर पर यह dbms द्वारा अनुकूलित किया जाएगा।
टिम श्मेल्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.