2009-03-15 13 views
39

मुझे एक पंक्ति की सामग्री में दिलचस्पी नहीं है, मैं सिर्फ यह जानना चाहता हूं कि कोई पंक्ति मौजूद है या नहीं। Name कॉलम एक प्राथमिक कुंजी है, इसलिए या तो 0 या 1 मिलान पंक्तियां होंगी। वर्तमान में, मैं उपयोग कर रहा हूँ:यह निर्धारित करने का सबसे तेज़ तरीका क्या है कि LINQ से SQL का उपयोग कर कोई पंक्ति मौजूद है या नहीं?

if ((from u in dc.Users where u.Name == name select u).Count() > 0) 
    // row exists 
else 
    // row doesn't exist 

जबकि ऊपर काम करता है, यह पंक्ति की सभी सामग्री चुनें (यदि वह मौजूद है) द्वारा अनावश्यक बहुत काम करता है। क्या निम्न एक तेज क्वेरी बनाते हैं:

if (dc.Users.Where(u => u.Name == name).Any()) 

... या क्या कोई तेज क्वेरी है?

उत्तर

73

Count() दृष्टिकोण अतिरिक्त कार्य कर सकता है, क्योंकि (टीएसक्यूएल में) EXISTS या TOP 1 अक्सर बहुत तेज़ होते हैं; डीबी अनुकूलित कर सकता है "कम से कम एक पंक्ति है"। व्यक्तिगत रूप से, मैं किसी भी/विधेय अधिभार का प्रयोग करेंगे:

if (dc.Users.Any(u => u.Name == name)) {...} 
बेशक

, आप TSQL देख कर की तुलना कर सकते क्या हर एक करता है:

dc.Log = Console.Out; 
+2

आपके लिए अतिरिक्त अपवोट सिर्फ इसलिए कि मुझे कभी एहसास नहीं हुआ कि लिंक से एसक्यूएल के लिए एक एसक्यूएल लॉगिंग सुविधा थी। मुझे इस पूरे समय एसक्यूएल प्रोफाइलर चलाने के लिए किया गया है। – David

+7

@ डेविड - वास्तव में। मैं एमएस डेटा टीम को ईएफ के लिए जोड़ने के लिए कहता हूं ;- –

+0

क्या होगा यदि आप डेटाबेस के लिए दूसरी क्वेरी लिखने के बजाय ** बाद में उपयोगकर्ताओं का उपयोग करना चाहते हैं? [यह] जांचें (http://stackoverflow.com/a/1071063/2218697), उम्मीद किसी की मदद करती है। – stom

2

मुझे लगता है कि:

if (dc.Users.Any(u => u.Name == name)) {...} 

सबसे अच्छा तरीका है।

0

मैं इस बात से सहमत नहीं हूं कि शीर्ष 1 का चयन हमेशा सभी SQL कार्यान्वयन के लिए चयन गिनती से बेहतर प्रदर्शन करेगा। यह सभी कार्यान्वयन निर्भर है, आप जानते हैं। उत्सुकता से, किसी विशेष डेटाबेस में संग्रहीत डेटा की प्रकृति भी समग्र परिणाम को प्रभावित करती है।

चलो उन दोनों की जांच करें जिस तरह से मैं उन्हें लागू करूँगा यदि मैं ऐसा करना चाहता हूं: दोनों मामलों के लिए, प्रक्षेपण (WHERE क्लॉज) मूल्यांकन एक आम कदम है।

अगला शीर्ष 1 के लिए अगला, आपको सभी फ़ील्ड का एक पठन करना होगा (जब तक कि आपने शीर्ष 1 'x' का चयन नहीं किया है उदाहरण: शीर्ष 1 1 का चयन करें)। यह कार्यात्मक रूप से IQueryable.Any (...) के बराबर होगा। सिवाय इसके कि आप EXISTS के पहले सामना किए गए रिकॉर्ड के प्रत्येक कॉलम के मान में चमकते समय कुछ समय बिताएंगे। यदि कथन टॉप में कथन में पाया जाता है, तो अगर कोई प्रोजेक्शन प्रोजेक्ट नहीं है (उदाहरण के लिए ऑर्डर द्वारा ऑर्डर) तो प्रक्षेपण छंटनी है। इस प्रीप्रोसेस में एक छोटी सी लागत होती है लेकिन अगर कोई रिकॉर्ड मौजूद नहीं होता है तो यह अतिरिक्त लागत है, इस मामले में, एक पूर्ण परियोजना अभी भी की जाती है।

चुनिंदा गणना के लिए, प्रीप्रोसेस नहीं किया जाता है। एक प्रक्षेपण किया जाता है और यदि EXISTS गलत है, तो परिणाम तत्काल है। यदि EXISTS सत्य है, तो गिनती अभी भी तेज है क्योंकि यह केवल dW_Highest_Inclusive - dW_Lowest_Exclusive होगा। 500 - 26 जितनी जल्दी हो। यदि मौजूद है तो झूठा है, नतीजा और भी तत्काल है।

शेष मामला इसलिए है: प्रक्षेपण कितना तेज़ है और आप पूर्ण प्रक्षेपण करके क्या खो देते हैं? और उत्तर यहां सबसे महत्वपूर्ण मुद्दे की ओर जाता है जो है: [NAME] फ़ील्ड अनुक्रमित है या नहीं! यदि आपके पास [NAME] पर कोई अनुक्रमणिका है तो किसी भी क्वेरी का प्रदर्शन इतना करीब होगा कि यह डेवलपर की प्राथमिकता के लिए उबलता है।

बड़े पैमाने पर, मैं बस पहले और बाद में दो से चार linq प्रश्नों और आउटपुट अंतर को लिखूंगा।

  1. चयन गिनती
  2. चयन शीर्ष 1
  3. चयन शीर्ष 1 1
  4. [NAME] ने एक nonclustered अनुक्रमणिका के साथ किसी भी

दोहराएँ सभी 4 का चयन करें;

8
कॉर्स

if (dc.Users.Where(u => u.Name == name).Any()) 

यह सबसे अच्छा है और यदि एक से अधिक की स्थिति तो जाँच करने के लिए यह रूप में

आप कंपनी तो

if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any()) 
1
के लिए उपयोगकर्ता की जांच करना चाहते कहो लिखने के लिए बहुत आसान है

उन लोगों के लिए जो किसी का दावा करते हैं() आगे बढ़ने का तरीका है मैंने लिंकपैड में कॉमनपैसवर्ड के एसक्यूएल डेटाबेस के खिलाफ एक सरल परीक्षण किया है, 14 मिलियन दे या ले लो। कोड:

var password = "qwertyuiop123"; 

var startTime = DateTime.Now; 
"From DB:".Dump(); 
startTime = DateTime.Now; 

if (CommonPasswords.Any(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password))) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

"From DB:".Dump(); 
startTime = DateTime.Now; 
if (CommonPasswords.Where(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password)).Count() > 0) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

"From DB:".Dump(); 
startTime = DateTime.Now; 
if (CommonPasswords.Where(c => c.Word.ToLower() == password).Take(1).Any()) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

यहाँ अनुवाद एसक्यूएल है:

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT 
    (CASE 
     WHEN EXISTS(
      SELECT NULL AS [EMPTY] 
      FROM [Security].[CommonPasswords] AS [t0] 
      WHERE [t0].[Word] LIKE @p0 
      ) THEN 1 
     ELSE 0 
    END) AS [value] 
GO 

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT COUNT(*) AS [value] 
FROM [Security].[CommonPasswords] AS [t0] 
WHERE [t0].[Word] LIKE @p0 
GO 

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT 
    (CASE 
     WHEN EXISTS(
      SELECT NULL AS [EMPTY] 
      FROM (
       SELECT TOP (1) NULL AS [EMPTY] 
       FROM [Security].[CommonPasswords] AS [t0] 
       WHERE LOWER([t0].[Word]) = @p0 
       ) AS [t1] 
      ) THEN 1 
     ELSE 0 
    END) AS [value] 

आप देख सकते हैं कि किसी भी प्रश्न कोड की एक और परत में लपेटता कहाँ तो मौजूद है 1 एक मामला ऐसा करने के लिए जहां काउंट का() बस एक गणना कमांड में जोड़ता है। इन दोनों के साथ समस्या आप एक शीर्ष (1) ऐसा नहीं कर सकते, लेकिन मैं शीर्ष का उपयोग कर एक बेहतर तरीका नहीं देख सकता है (1)

परिणाम:

डीबी से: पाया: प्रसंस्करण समय: 13.3962

डीबी से

: पाया: प्रसंस्करण समय: 12,0933

डीबी से: पाया: प्रसंस्करण समय: 787,8801

फिर:

डीबी से: पाया: प्रसंस्करण समय: 13,3878

डीबी से: पाया: प्रसंस्करण समय: 12,6881

डीबी से: पाया: प्रसंस्करण समय: 780,2686

फिर:

डीबी से: फाउंड: प्रसंस्करण समय: 24.7081

डीबी से: पाया: प्रसंस्करण समय: 23,6654

डीबी से: पाया: प्रसंस्करण समय: 699,622

सूचकांक के बिना:

डीबी से: पाया: प्रसंस्करण समय: 2395।1988

डीबी से: पाया: प्रसंस्करण समय: 390,6334

डीबी से: पाया: प्रसंस्करण समय: 664,8581

अब आप में से कुछ सोच सकते है कि यह केवल एक या दो मिलीसेकंड है। हालांकि इससे पहले कि मैं इस पर एक इंडेक्स डालूं, भिन्नता बहुत अधिक थी; कुछ सेकंड तक।

आखिरी गणना वहां है क्योंकि मैंने इस धारणा के साथ शुरुआत की है कि ToLower() LIKE से तेज़ होगा, और जब तक मैंने गिनने की कोशिश नहीं की और तब तक एक इंडेक्स डाला, तब तक मैं सही था। मुझे लगता है कि लोअर() सूचकांक को अपरिवर्तनीय बनाता है।

संबंधित मुद्दे

 संबंधित मुद्दे