2011-09-20 11 views
14

हम इन प्रश्नों के बीच एक बड़ा अंतर देख रहे हैं।एसक्यूएल क्यों चुनें COUNT (*), MIN (col), MAX (col) तेज़ है तो चयन करें MIN (col), MAX (col)

धीमी क्वेरी

SELECT MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193 

टेबल 'तालिका'। स्कैन की संख्या 2, तार्किक 2,458,969 पढ़ता है, शारीरिक पढ़ता 0, पढ़ने के लिए आगे पढ़ता 0, कार्य तार्किक पढ़ता 0, कार्य शारीरिक पढ़ता 0, lob-पढ़ने के लिए आगे पढ़ता 0.

एसक्यूएल सर्वर निष्पादन समय: CPU समय = 1966 एमएस , समय बीत गया = 1 9 55 एमएस।

तेजी से क्वेरी

SELECT count(*), MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193 

टेबल 'तालिका'। स्कैन गिनती 1, तार्किक 5803 पढ़ता है, शारीरिक पढ़ता 0, पढ़ने के लिए आगे पढ़ता 0, कार्य तार्किक पढ़ता 0, कार्य शारीरिक पढ़ता 0, lob-पढ़ने के लिए आगे पढ़ता 0.

एसक्यूएल सर्वर निष्पादन समय: CPU समय = 0 एमएस , समय बीत गया = 9 एमएस।

प्रश्न

प्रश्नों के बीच भारी अंतर के बीच प्रदर्शन कारण क्या है?

अद्यतन एक छोटी सी अद्यतन टिप्पणी के रूप में दिए गए प्रश्नों पर आधारित:

निष्पादन या बार-बार निष्पादन के आदेश बुद्धिमान कुछ भी नहीं प्रदर्शन बदल जाता है। कोई अतिरिक्त पैरामीटर उपयोग नहीं किया जाता है और (परीक्षण) डेटाबेस निष्पादन के दौरान कुछ और नहीं कर रहा है।

धीरे क्वेरी

|--Nested Loops(Inner Join) 
|--Stream Aggregate(DEFINE:([Expr1003]=MIN([DBTest].[dbo].[table].[startdate]))) 
    | |--Top(TOP EXPRESSION:((1))) 
    |   |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1008]) WITH ORDERED PREFETCH) 
    |    |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED FORWARD) 
    |    |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]), WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD) 
    |--Stream Aggregate(DEFINE:([Expr1004]=MAX([DBTest].[dbo].[table].[startdate]))) 
     |--Top(TOP EXPRESSION:((1))) 
      |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1009]) WITH ORDERED PREFETCH) 
        |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED BACKWARD) 
        |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]), WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD) 

फास्ट क्वेरी

|--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1012],0))) 
    |--Stream Aggregate(DEFINE:([Expr1012]=Count(*), [Expr1004]=MIN([DBTest].[dbo].[table].[startdate]), [Expr1005]=MAX([DBTest].[dbo].[table].[startdate]))) 
     |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1011]) WITH UNORDERED PREFETCH) 
      |--Index Seek(OBJECT:([DBTest].[dbo].[table].[FK]), SEEK:([DBTest].[dbo].[table].[FK]=(5806)) ORDERED FORWARD) 
      |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]), WHERE:([DBTest].[dbo].[table].[status]<'A' OR [DBTest].[dbo].[table].[status]>'A') LOOKUP ORDERED FORWARD) 

The execution plan from SSMS

उत्तर

जवाब से नीचे दिए गए मार्टिन स्मिथ समस्या की व्याख्या करने लगता है। सुपर लघु संस्करण यह है कि एमएस-एसक्यूएल क्वेरी-विश्लेषक धीमी क्वेरी में एक क्वेरी प्लान का गलत उपयोग करता है जो एक पूर्ण तालिका स्कैन का कारण बनता है।

एक गणना (*) जोड़ना, प्रारंभिक संकेत (फोरसेकैन) या स्टार्टडेट पर एक संयुक्त सूचकांक, एफके और स्टेटस कॉलम प्रदर्शन समस्या को हल करता है।

+2

यदि आप दूसरी क्वेरी के बाद पहली क्वेरी चलाते हैं तो क्या होगा? – gbn

+1

शायद क्योंकि जब आप गिनती (*) का उपयोग कर रहे हैं तो आप fk = 4193 के लिए प्रत्येक रिकॉर्ड की जांच नहीं करते? – nosbor

+1

क्या आप इन्हें दूसरे के बाद चला रहे हैं? यदि हां: तो क्या होगा यदि आप दोनों प्रश्नों से पहले 'डीबीसीसी ड्रॉप्लेनबफर' और 'डीबीसीसी फ्रीप्रोकैच 'डाल दें? क्या होता है यदि आप अनुक्रम बदलते हैं - पहले तेज क्वेरी चलाएं, फिर धीमी गति से? –

उत्तर

24

एसक्यूएल सर्वर प्रमुखता आकलनकर्ता विभिन्न मॉडलिंग धारणाएं बनाता है इस तरह के

  • के रूप में स्वतंत्रता: विभिन्न स्तंभों पर डेटा वितरण स्वतंत्र हैं, जब तक सहसंबंध जानकारी उपलब्ध है।
  • समानता: प्रत्येक आंकड़े ऑब्जेक्ट हिस्टोग्राम चरण के भीतर, अलग-अलग मान समान रूप से फैले हुए होते हैं और प्रत्येक मान में समान आवृत्ति होती है।

Source

तालिका में 810,064 पंक्तियों रहे हैं।

आप क्वेरी

SELECT COUNT(*), 
     MIN(startdate) AS Firstdate, 
     MAX(startdate) AS Lastdate 
FROM table 
WHERE status <> 'A' 
     AND fk = 4193 

1.893 (0,23%) पंक्तियों fk = 4193 विधेय को पूरा किया है, और उन दो असफल status <> 'A' हिस्सा इतना समग्र 1,891 मैच और जरूरत के एकत्रित होने की।

आपके पास दो इंडेक्स भी हैं जिनमें से कोई भी संपूर्ण क्वेरी को कवर नहीं करता है।

अपने तेजी से क्वेरी के लिए यह fk पर एक सूचकांक का उपयोग करता है सीधे पंक्तियों जहां fk = 4193 तो 1,893 key lookups करने के लिए status विधेय की जाँच करें और एकत्रीकरण के लिए startdate पुनः प्राप्त करने के क्लस्टर सूचकांक में प्रत्येक पंक्ति को खोजने के लिए की जरूरत है खोजने के लिए।

जब आप नहीं रह SELECT सूची एसक्यूएल सर्वर से COUNT(*) को दूर हर क्वालीफाइंग पंक्ति पर कार्रवाई करने के है। नतीजतन यह एक और विकल्प मानता है।

आप MAX कर सकते हैं startdate पर एक सूचकांक तो यह स्कैनिंग शुरू कर सकता है कि शुरू से ही, के रूप में यह पहली मिलान पंक्ति रोक पाता कुंजी लुकअप कर आधार तालिका में वापस और जैसे ही यह MIN(startdate) पाया गया है, इसी तरह की है इंडेक्स के दूसरे छोर से शुरू करने और पीछे की ओर काम करने वाले दूसरे स्कैन के साथ मिलें।

एसक्यूएल सर्वर का अनुमान है कि इनमें से प्रत्येक स्कैन भविष्यवाणी से मेल खाने वाले एक पर हिट करने से पहले 5 9 0 पंक्तियों को संसाधित कर देगा। 1,880 कुल लुकअप बनाम 1,8 9 3 दे रहा है, इसलिए यह इस योजना को चुनता है।

590 आंकड़ा सिर्फ table_size/estimated_number_of_rows_that_match है। यानी कार्डिनालिटी अनुमानक मानता है कि मेल खाने वाली पंक्तियों को पूरे टेबल में समान रूप से वितरित किया जाएगा।

दुर्भाग्यवश 1,8 9 1 पंक्तियां जो अनुमान को पूरा करती हैं startdate के संबंध में यादृच्छिक रूप से वितरित नहीं होती हैं। वास्तव में वे सभी इंडेक्स के अंत की ओर एक एकल 8,205 पंक्ति खंड में घिरे हुए हैं जिसका अर्थ है कि स्कैन MIN(startdate) तक पहुंचने से पहले 801,85 9 कुंजी लुकअप करने से पहले समाप्त हो जाता है।

इसे नीचे पुन: उत्पन्न किया जा सकता है।

CREATE TABLE T 
(
id int identity(1,1) primary key, 
startdate datetime, 
fk int, 
[status] char(1), 
Filler char(2000) 
) 

CREATE NONCLUSTERED INDEX ix ON T(startdate) 

INSERT INTO T 
SELECT TOP 810064 Getdate() - 1, 
        4192, 
        'B', 
        '' 
FROM sys.all_columns c1, 
     sys.all_columns c2 


UPDATE T 
SET fk = 4193, startdate = GETDATE() 
WHERE id BETWEEN 801859 and 803748 or id = 810064 

UPDATE T 
SET startdate = GETDATE() + 1 
WHERE id > 810064 


/*Both queries give the same plan. 
UPDATE STATISTICS T WITH FULLSCAN 
makes no difference*/ 

SELECT MIN(startdate) AS Firstdate, 
     MAX(startdate) AS Lastdate 
FROM T 
WHERE status <> 'A' AND fk = 4192 


SELECT MIN(startdate) AS Firstdate, 
     MAX(startdate) AS Lastdate 
FROM T 
WHERE status <> 'A' AND fk = 4193 

आप क्वेरी का उपयोग कर विचार कर सकते हैं बल्कि startdate से fk पर सूचकांक का उपयोग करें या इस समस्या से बचने के लिए जोड़ (fk,status) INCLUDE (startdate) पर सुझाव लापता सूचकांक कार्य योजना लागू में प्रकाश डाला करने के लिए योजना के लिए मजबूर करने का संकेत।

+0

से साफ़ कर दिया गया है क्वेरी से स्थिति कॉलम ड्रॉप करना दोनों प्रश्नों को दो प्रतिशत नीचे धीमा कर देता है। – CodingBarfield

+0

ऑटो बनाएं सांख्यिकी/ऑटो अपडेट आंकड़े सही हैं – CodingBarfield

+0

निष्पादन योजना एक्सएमएल http://pastebin.com/mBcgHYkN हम अब जांच कर रहे हैं कि यह एक गैर-रखरखाव योजना हो सकती है। – CodingBarfield