2015-01-19 10 views
5

क्या कोई जानता है कि एसक्यूएल सर्वर टेबल 'बिल्डिंग' से दो बार पूछताछ क्यों करता है? क्या कोई स्पष्टीकरण है? क्या यह केवल एक टेबल के साथ किया जा सकता है? दो क्लस्टर सूचकांक की तलाश मर्ज के माध्यम से एकीकृत शामिल हों (संयोजन):एसक्यूएल सर्वर - एक ही टेबल के लिए स्कैनिंग दो बार क्यों किया जाता है?

DECLARE @id1stBuild INT = 1 
    ,@number1stBuild INT = 2 
    ,@idLastBuild INT = 5 
    ,@numberLastBuild INT = 1; 
DECLARE @nr TABLE (nr INT); 

INSERT @nr 
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); 

CREATE TABLE building (
    id INT PRIMARY KEY identity(1, 1) 
    ,number INT NOT NULL 
    ,idStreet INT NOT NULL 
    ,surface INT NOT NULL 
    ) 

INSERT INTO building (number,idStreet,surface) 
SELECT bl.b 
    ,n.nr 
    ,abs(convert(BIGINT, convert(VARBINARY, NEWID()))) % 500 
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY n1.nr) b 
    FROM @nr n1 
    CROSS JOIN @nr n2 
    CROSS JOIN @nr n3 
    ) bl 
CROSS JOIN @nr n 

--***** execution plan for the select below 
SELECT * 
FROM building b 
WHERE b.id = @id1stBuild 
    AND b.number = @number1stBuild 
    OR b.id = @idLastBuild 
    AND b.number = @numberLastBuild 

DROP TABLE building 

इस के लिये कार्य योजना हमेशा एक ही है:

इस कोड नमूना है। बाकी कम महत्वपूर्ण है।

enter image description here

+2

के बराबर आपका जहां खंड एक ब्रैकेट याद आ रही है है। मुझे लगता है कि इसका कारण हो सकता है ... '([email protected] और [email protected]) या ([email protected] और [email protected])' '' 'के कारण –

+0

त्वरित उत्तर के लिए धन्यवाद। मैंने कोशिश की और यह अभी भी एक ही निष्पादन योजना है। – Emarian

उत्तर

2

आप निम्न का प्रयास कर सकते हैं, जो केवल एक ही खोज और थोड़ा प्रदर्शन सुधार देता है। @Martin_Smith कहते हैं कि तुम क्या कोडित है एक Union

SELECT * 
FROM building b 
WHERE b.id IN (@id1stBuild , @idLastBuild) 
    AND 
     (
      (b.id = @id1stBuild AND b.number = @number1stBuild) OR 
      (b.id = @idLastBuild AND b.number = @numberLastBuild) 
     ) 
+1

यह एक एकल इटेटरेटर की तलाश करता है लेकिन वह अभी भी दो खोज करता है। [जब कोई तलाश नहीं है?] (Http://sqlblog.com/blogs/paul_white/archive/2011/02/16/when-is-a-seek-not-a-seek.aspx) –

+0

@ मार्टिनस्मिथ इस मामले में क्वेरी प्लान बताता है कि आईएन क्लॉज में पैरामीटर सॉर्ट किए गए हैं, फिर SEEK predicate एक श्रेणी स्कैन का उपयोग करता है जहां id> start और id

+0

यह डुप्लिकेट को खत्म करने के लिए एक मर्ज अंतराल का उपयोग करता है। खोज दो बार निष्पादित की जाती है। वास्तविक निष्पादन योजना में "निष्पादन की संख्या" देखें। –

5

यह दो बार स्कैनिंग नहीं कर रहा है: यहाँ कार्य योजना लागू है। यह दो बार मांग रहा है।

आपकी क्वेरी अर्थात् नीचे जैसा ही है।

SELECT * 
FROM building b 
WHERE b.id = @id1stBuild 
     AND b.number = @number1stBuild 
UNION 
SELECT * 
FROM building b 
WHERE b.id = @idLastBuild 
     AND b.number = @numberLastBuild 

और निष्पादन योजना दो खोजों और संघों को परिणाम देती है।

+0

मैंने 'यूनियन' के रूप में 'OR' ऑपरेटर को देखने का कभी सोचा नहीं, लेकिन यह वास्तव में बहुत समझ में आता है। +1 –

+1

'यूनियन' नहीं है 'यूनियन ऑल' और यह * बहुत * महत्वपूर्ण है। सोचें कि क्या परिणाम है यदि '@ id1stBuild'' idlastBuild' के बराबर है और '@ number1stBuild'' @ numberLastBuild' के बराबर है। या क्वेरी एक पंक्ति देता है, जबकि यूनियन सभी दो पंक्तियां देता है। –

+0

@RemusRusanu - अच्छा सुधार। –

3

एक ही टेबल के लिए स्कैनिंग दो बार क्यों किया जाता है?

स्कैन नहीं है, एक तलाश है, और इससे सभी फर्क पड़ता है।

कार्यान्वयन या यूनियन के रूप में, और उसके बाद यूनियन जॉइन के माध्यम से यूनियन को कार्यान्वित करना।

मर्ज संघ

अब के प्रश्न थोड़ा बदल:

select a from T where b = 1 or c = 3 

    |--Stream Aggregate(GROUP BY:([T].[a])) 
    |--Merge Join(Concatenation) 
     |--Index Seek(OBJECT:([T].[Tb]), SEEK:([T].[b]=(1)) ORDERED FORWARD) 
     |--Index Seek(OBJECT:([T].[Tc]), SEEK:([T].[c]=(3)) ORDERED FORWARD) 
संयोजन के बजाय

और तरह अलग ऑपरेटरों, अब हम किसी मर्ज में शामिल होने (संयोजन एक 'merge union' कहा जाता है) और एक धारा कुल। क्या हुआ? मर्ज जॉइन (कॉन्सटेनेशन) या "मर्ज यूनियन" वास्तव में वास्तव में शामिल नहीं है। यह एक ही इटरेटर द्वारा विलय में शामिल होने के रूप में लागू किया जाता है, लेकिन यह इनपुट पंक्तियों के क्रम को संरक्षित करते समय वास्तव में एक संघ करता है। अंत में, हम डुप्लीकेट को खत्म करने के लिए कुल धारा का उपयोग करते हैं। (डुप्लिकेट को खत्म करने के लिए स्ट्रीम कुल का उपयोग करने के बारे में अधिक जानकारी के लिए इस पोस्ट को देखें।) यह योजना आम तौर पर एक बेहतर विकल्प है क्योंकि सॉर्ट अलग-अलग मेमोरी का उपयोग करता है और अगर यह स्मृति से बाहर हो जाता है तो डेटा को डिस्क में फैला सकता है जबकि धारा कुल स्मृति का उपयोग नहीं करता है ।

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