कौन सी SQL क्वेरी तेज है? फ़िल्टर मापदंड में शामिल हों या खंड कहाँ है?


97

इन 2 प्रश्नों की तुलना करें। क्या फिल्टर को जॉइन मानदंड पर या WHEREक्लॉज में डालना ज्यादा तेज है । मैंने हमेशा महसूस किया है कि यह शामिल होने के मानदंडों पर तेज़ है क्योंकि यह परिणाम को जल्द से जल्द निर्धारित करता है, लेकिन मुझे यकीन नहीं है।

मैं देखने के लिए कुछ परीक्षण बनाने जा रहा हूं, लेकिन मैं भी ऐसी राय प्राप्त करना चाहता था जिस पर पढ़ने के लिए स्पष्ट हो।

प्रश्न 1

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
INNER JOIN  TableB b
        ON  x.TableBID = b.ID
WHERE       a.ID = 1            /* <-- Filter here? */

प्रश्न २

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
        AND a.ID = 1            /* <-- Or filter here? */
INNER JOIN  TableB b
        ON  x.TableBID = b.ID

संपादित करें

मैंने कुछ परीक्षण चलाए और परिणाम बताते हैं कि यह वास्तव में बहुत करीब है, लेकिन WHEREखंड वास्तव में थोड़ा तेज है! =)

मैं इस बात से पूरी तरह सहमत हूं कि WHEREक्लॉज़ पर फ़िल्टर को लागू करने के लिए यह अधिक समझ में आता है , मैं प्रदर्शन के निहितार्थ के लिए उत्सुक था।

बीता हुआ समय कहां मानदंड: 143,016 एमएस
बीता हुआ समय में शामिल हों मानदंड: 143,256 एमएस

परीक्षा

SET NOCOUNT ON;

DECLARE @num    INT,
        @iter   INT

SELECT  @num    = 1000, -- Number of records in TableA and TableB, the cross table is populated with a CROSS JOIN from A to B
        @iter   = 1000  -- Number of select iterations to perform

DECLARE @a TABLE (
        id INT
)

DECLARE @b TABLE (
        id INT
)

DECLARE @x TABLE (
        aid INT,
        bid INT
)

DECLARE @num_curr INT
SELECT  @num_curr = 1
        
WHILE (@num_curr <= @num)
BEGIN
    INSERT @a (id) SELECT @num_curr
    INSERT @b (id) SELECT @num_curr
    
    SELECT @num_curr = @num_curr + 1
END

INSERT      @x (aid, bid)
SELECT      a.id,
            b.id
FROM        @a a
CROSS JOIN  @b b

/*
    TEST
*/
DECLARE @begin_where    DATETIME,
        @end_where      DATETIME,
        @count_where    INT,
        @begin_join     DATETIME,
        @end_join       DATETIME,
        @count_join     INT,
        @curr           INT,
        @aid            INT

DECLARE @temp TABLE (
        curr    INT,
        aid     INT,
        bid     INT
)

DELETE FROM @temp

SELECT  @curr   = 0,
        @aid    = 50

SELECT  @begin_where = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
    INNER JOIN  @b b
            ON  x.bid = b.id
    WHERE       a.id = @aid
        
    SELECT @curr = @curr + 1
END
SELECT  @end_where = CURRENT_TIMESTAMP

SELECT  @count_where = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @curr = 0
SELECT  @begin_join = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
            AND a.id = @aid
    INNER JOIN  @b b
            ON  x.bid = b.id
    
    SELECT @curr = @curr + 1
END
SELECT  @end_join = CURRENT_TIMESTAMP

SELECT  @count_join = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @count_where AS count_where,
        @count_join AS count_join,
        DATEDIFF(millisecond, @begin_where, @end_where) AS elapsed_where,
        DATEDIFF(millisecond, @begin_join, @end_join) AS elapsed_join

9
डेटा के आधार पर, WHERE बनाम JOIN मानदंड अलग-अलग परिणाम दे सकते हैं।
OMG पॉनीज़

3
@OMG पॉनी बहुत सही है, लेकिन बहुत बार यह भी नहीं होता है।
जॉन एरिकसन

2
मैं अंतर को 5% अंतर नहीं कहूंगा- वे समान हैं। आप 2% के अंतर के लिए महत्व चाहते हैं, यह सुनिश्चित करने के लिए 1000 बार परीक्षण बेहतर ढंग से चलाएं कि यह सिर्फ यादृच्छिक नहीं है।
टॉमटॉम

लाभ अगर यह था x.ID तो आप अधिक होने की संभावना एक a.ID साथ की तुलना में सुधार देखने के लिए होगा तो शामिल होने से पहले डेटा को फ़िल्टर में है
MikeT

जवाबों:


64

प्रदर्शन-वार, वे समान हैं (और समान योजनाएं बनाते हैं)

तार्किक रूप से, आपको उस ऑपरेशन को करना चाहिए जो अभी भी समझ में आता है यदि आप INNER JOINएक के साथ प्रतिस्थापित करते हैं LEFT JOIN

आपके मामले में यह इस तरह दिखेगा:

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
        AND a.ID = 1
LEFT JOIN
        TableB b
ON      x.TableBID = b.ID

या यह:

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
LEFT JOIN
        TableB b
ON      b.id = x.TableBID
WHERE   a.id = 1

पूर्व क्वेरी a.idअन्य के लिए कोई वास्तविक मैच नहीं लौटाएगी 1, इसलिए बाद वाला वाक्यविन्यास WHEREतार्किक रूप से अधिक सुसंगत है।


जब मैं सेट खींचता हूं तो मुझे समझ में आता है कि दूसरा मामला अधिक सुसंगत क्यों है। पूर्व प्रश्न में, बाधा a.id = 1केवल चौराहे के लिए लागू होती है, न कि बाएं हिस्से को चौराहे को छोड़कर।
फीटहेबुल्ट

1
पहले उदाहरण में पंक्तियाँ हो सकती हैं जहाँ a.id != 1, दूसरे में केवल पंक्तियाँ होंगी जहाँ a.id = 1
FtheBuilder

1
आपकी भाषा अस्पष्ट है। "तार्किक रूप से, आपको उस ऑपरेशन को करना चाहिए जो अभी भी समझ में आता है अगर ..." और "तार्किक रूप से अधिक सुसंगत" समझ में नहीं आता है। क्या आप कृपया पुनःप्रकाश कर सकते हैं?
फिलीपिक्सी

23

आंतरिक जुड़ावों के लिए यह कोई मायने नहीं रखता है कि आपने अपना मापदंड कहाँ रखा है। SQL कंपाइलर एक एक्जीक्यूशन प्लान में दोनों को बदल देगा जिसमें फ़िल्टरिंग जॉइन के नीचे होता है (जैसे कि फिल्टर एक्सप्रेशन में शामिल होने की स्थिति में है)।

बाहरी जोड़ एक अलग मामला है, क्योंकि फ़िल्टर की जगह क्वेरी के शब्दार्थ को बदल देती है।


तो अंदरूनी जोड़ में यह पहले फिल्टर की गणना करता है और फिर दूसरी तालिका के साथ फ़िल्टर के आउटपुट में शामिल होता है या क्या यह पहले दो तालिकाओं में शामिल होता है और फिर फ़िल्टर को लागू करता है?
अश्विन

@Remus Rusanu - क्या आप इस बारे में विस्तार से बता सकते हैं कि बाहरी जुड़ने की स्थिति में शब्दार्थ कैसे बदल जाता है? मुझे फ़िल्टर की स्थिति के आधार पर अलग-अलग परिणाम मिलते हैं, लेकिन यह समझने में असमर्थ हैं कि
अनंत

3
@ एक बाहरी जुड़ने के साथ आप जॉइन की गई स्थिति से मेल नहीं खाने वाले जॉइन किए गए टेबल के सभी कॉलम के लिए NULLs प्राप्त करते हैं। फ़िल्टर NULL को संतुष्ट नहीं करेंगे और पंक्तियों को खत्म करते हुए, OUTER को एक INNER जॉइन में शामिल करते हुए मोड़ देंगे।
रेमस रूसु

10

जहाँ तक दो विधियाँ जाती हैं।

  • जॉइन / ऑन टेबल में शामिल होने के लिए है
  • वहाँ फ़िल्टरिंग परिणामों के लिए है

जब तक आप उन्हें अलग तरह से इस्तेमाल कर सकते हैं, यह हमेशा मेरे लिए एक गंध की तरह लगता है।

समस्या होने पर प्रदर्शन से निपटें। तब आप इस तरह के "अनुकूलन" पर गौर कर सकते हैं।


2

किसी भी क्वेरी ऑप्टिमाइज़र worh a सेंट के साथ .... वे समान हैं।


मुझे पूरा यकीन है कि, किसी भी वास्तविक कार्यभार के साथ, वे समान नहीं हैं। यदि आपके पास लगभग डेटा नहीं है, तो सवाल बेकार है।
eKek0

2
वास्तविक कार्यभार के तहत इसे देखें। मूल रूप से - यदि वे एक ही निष्पादन योजना उत्पन्न करते हैं, तो वे ... प्रदर्शन में समान हैं। कम से कम सामान्य / सरल मामलों के लिए (अर्थात 14 तालिकाओं में शामिल नहीं होने वाला) मुझे पूरा यकीन है कि वे समान हैं?)
टॉमटॉम

1

Postgresql में वे समान हैं। हम यह जानते हैं क्योंकि यदि आप explain analyzeप्रत्येक प्रश्न पर करते हैं, तो योजना समान होती है। इस उदाहरण को लें:

# explain analyze select e.* from event e join result r on e.id = r.event_id and r.team_2_score=24;

                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=27.09..38.22 rows=7 width=899) (actual time=0.045..0.047 rows=1 loops=1)
   Hash Cond: (e.id = r.event_id)
   ->  Seq Scan on event e  (cost=0.00..10.80 rows=80 width=899) (actual time=0.009..0.010 rows=2 loops=1)
   ->  Hash  (cost=27.00..27.00 rows=7 width=8) (actual time=0.017..0.017 rows=1 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on result r  (cost=0.00..27.00 rows=7 width=8) (actual time=0.006..0.008 rows=1 loops=1)
               Filter: (team_2_score = 24)
               Rows Removed by Filter: 1
 Planning time: 0.182 ms
 Execution time: 0.101 ms
(10 rows)

# explain analyze select e.* from event e join result r on e.id = r.event_id where r.team_2_score=24;
                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=27.09..38.22 rows=7 width=899) (actual time=0.027..0.029 rows=1 loops=1)
   Hash Cond: (e.id = r.event_id)
   ->  Seq Scan on event e  (cost=0.00..10.80 rows=80 width=899) (actual time=0.010..0.011 rows=2 loops=1)
   ->  Hash  (cost=27.00..27.00 rows=7 width=8) (actual time=0.010..0.010 rows=1 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on result r  (cost=0.00..27.00 rows=7 width=8) (actual time=0.006..0.007 rows=1 loops=1)
               Filter: (team_2_score = 24)
               Rows Removed by Filter: 1
 Planning time: 0.140 ms
 Execution time: 0.058 ms
(10 rows)

वे दोनों एक ही न्यूनतम और अधिकतम लागत के साथ-साथ एक ही क्वेरी योजना के हैं। इसके अलावा, ध्यान दें कि शीर्ष क्वेरी में भी team_score_2 एक 'फ़िल्टर' के रूप में लागू होता है।


0

यह वास्तव में संभावना नहीं है कि इस जॉइन का प्लेसमेंट प्रदर्शन के लिए निर्णायक कारक होगा। मैं tsql के लिए निष्पादन योजना से परिचित नहीं हूं, लेकिन यह संभावना है कि वे समान योजनाओं के लिए स्वचालित रूप से अनुकूलित होंगे।


0

नियम # 0: कुछ मानदंड चलाएं और देखें! वास्तव में यह बताने का एकमात्र तरीका है कि कौन सा प्रयास तेज होगा। इस प्रकार के बेंचमार्क एसक्यूएल प्रोफाइलर का उपयोग करने के लिए बहुत आसान हैं।

इसके अलावा, जोइन के साथ लिखी गई क्वेरी के लिए निष्पादन योजना की जांच करें और यह देखने के लिए कि अंतर क्या है।

अंत में, जैसा कि दूसरों ने कहा है, इन दोनों को किसी भी सभ्य ऑप्टिमाइज़र द्वारा पहचाना जाना चाहिए, जिसमें SQL सर्वर बनाया गया है।


लेकिन केवल आंतरिक जुड़ाव के लिए। परिणाम सेट आउट जॉइन के लिए बहुत अलग होंगे।
HLGEM

बेशक। सौभाग्य से, प्रदान किए गए उदाहरण आंतरिक जोड़ का उपयोग करते हैं।
डीएवी

1
दुर्भाग्य से सवाल जुड़ने के बारे में है, न कि आंतरिक जुड़ावों के बारे में।
पॉल

हाँ डेविड, सवाल जुड़ने के बारे में है। प्रश्न का समर्थन करने वाला नमूना आंतरिक जोड़ का उपयोग करने के लिए होता है।
पॉल

0

क्या यह तेज है? यह कोशिश करो और देखो।

कौन सा पढ़ना आसान है? मेरे लिए पहला अधिक "सही" दिखता है, क्योंकि स्थानांतरित स्थिति वास्तव में जुड़ने के साथ कुछ भी नहीं है।


0

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

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