LEFT JOIN या NOT EXISTS का उपयोग करने के बीच सबसे अच्छा अभ्यास


67

क्या एक LEFT JOIN या NOT EXISTS प्रारूप का उपयोग करने के बीच एक सर्वोत्तम अभ्यास है?

एक के ऊपर एक प्रयोग करने से क्या लाभ है?

यदि कोई नहीं है, जिसे प्राथमिकता दी जानी चाहिए?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

मैं SQL सर्वर डेटाबेस के विरुद्ध पहुँच के भीतर क्वेरीज़ का उपयोग कर रहा हूँ।


2
एक अलग रूप में के रूप में, उचित रूप में समान दृष्टिकोण WHERE A.idx NOT IN (...) है नहीं समान का त्रिसंयोजक व्यवहार की वजह से NULL(यानी NULLके बराबर नहीं है NULL, (और न ही असमान) इसलिए यदि आपके पास किसी भी NULL में tableBआप अप्रत्याशित परिणाम मिल जाएगा!)
Elaskanator

जवाबों:


58

सबसे बड़ा अंतर नहीं है बनाम में मौजूद नहीं है, यह (जैसा लिखा गया है), है SELECT *

पहले उदाहरण पर, आपको दोनों A और से सभी कॉलम मिलते हैं B, जबकि दूसरे उदाहरण में, आपको केवल कॉलम मिलते हैं A

SQL सर्वर में, दूसरा संस्करण एक बहुत ही सरल आकस्मिक उदाहरण में थोड़ा तेज है:

दो नमूना तालिकाएँ बनाएँ:

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

प्रत्येक तालिका में 10,000 पंक्तियाँ डालें:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

हर 5 वीं पंक्ति को दूसरी तालिका से निकालें:

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

दो टेस्ट SELECTस्टेटमेंट वेरिएंट करें:

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

निष्पादन योजना:

यहाँ छवि विवरण दर्ज करें

दूसरे संस्करण को फ़िल्टर ऑपरेशन करने की आवश्यकता नहीं है क्योंकि यह बाएं एंटी-सेमी जॉइन ऑपरेटर का उपयोग कर सकता है।


23

तार्किक रूप से वे समान हैं, लेकिन NOT EXISTSएंटीसिमोनी के करीब हैं जो आप पूछ रहे हैं, और आमतौर पर पसंद किया जाता है। यह बेहतर ढंग से हाइलाइट करता है कि आप कॉलम को B में एक्सेस नहीं कर सकते, क्योंकि यह केवल एक फिल्टर के रूप में उपयोग किया जाता है (जैसा कि उन्हें NULL मान के साथ उपलब्ध होने के विपरीत)।

कई साल पहले (SQL Server 6.0 ish), LEFT JOINतेज था, लेकिन यह बहुत लंबे समय के लिए मामला नहीं रहा है। इन दिनों, NOT EXISTSथोड़ा तेज है।


एक्सेस में सबसे बड़ा प्रभाव यह है कि JOINविधि को फ़िल्टर करने से पहले जॉइन को पूरा करना पड़ता है, जिससे मेमोरी में शामिल सेट का निर्माण होता है। NOT EXISTSइसका उपयोग पंक्ति के लिए जाँच करता है, लेकिन कॉलम के लिए स्थान आवंटित नहीं करता है। साथ ही, यह एक पंक्ति को ढूंढते हुए देखना बंद कर देता है। प्रवेश में प्रदर्शन थोड़ा अधिक भिन्न होता है, लेकिन अंगूठे का एक सामान्य नियम यह है कि NOT EXISTSयह थोड़ा तेज हो जाता है। मुझे यह कहना कम पसंद होगा कि यह "सर्वोत्तम अभ्यास" है, क्योंकि इसमें और कारक शामिल हैं।


6

लिंक किए गए सर्वर का उपयोग करते समय एक अपवाद मैंने NOT EXISTSश्रेष्ठ (हालांकि मामूली रूप से) होने के लिए देखा है ।LEFT JOIN ... WHERE IS NULL

निष्पादन योजनाओं की जांच करने से, ऐसा प्रतीत होता है कि NOT EXISTSऑपरेटर नेस्टेड लूप फैशन में निष्पादित हो जाता है। जिससे इसे प्रति पंक्ति के आधार पर निष्पादित किया जाता है (जो मुझे लगता है कि समझ में आता है)।

उदाहरण निष्पादन योजना इस व्यवहार को प्रदर्शित करती है: यहाँ छवि विवरण दर्ज करें


1
लिंक किए गए सर्वर उस तरह के लिए क्रूर हैं। उस समस्या को हल करने के लिए एक संभावित दृष्टिकोण डेटाबेस के उस अस्थायी प्रतिलिपि के खिलाफ क्लॉज को INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=yचलाने वाले एक साधारण का उपयोग करके लिंक किए गए सर्वर लिंक पर दूरस्थ डेटा की NOT EXISTS (...)प्रतिलिपि बनाना है।
मैक्स वर्नोन

2
मेरे पोस्ट पर मैक्स वर्नोन से प्रतिक्रिया प्राप्त करने के लिए अभी थोड़ा बाशफुल! फैनबॉय एक तरफ। यह हास्यास्पद है कि आप इसका उल्लेख करते हैं, जैसा कि मैंने उन क्रॉस-सर्वर स्थितियों से सबसे अधिक प्राप्त करने के लिए कई अवसरों पर सटीक दृष्टिकोण का उपयोग किया है।
रोबोपिम

1
चीयर्स, @pimbrouwers - अपनी तरह की टिप्पणी के लिए धन्यवाद!
मैक्स वर्नोन

5

सामान्य तौर पर, इंजन अनिवार्य रूप से निम्न के आधार पर एक निष्पादन योजना बनाएगा:

  1. A और B में पंक्तियों की संख्या
  2. ए और / या बी पर सूचकांक है या नहीं
  3. परिणाम पंक्तियों की अपेक्षित संख्या (और मध्यवर्ती पंक्तियाँ)
  4. इनपुट क्वेरी का रूप (अर्थात आपका प्रश्न)

के लिए (4):

"बी मौजूद नहीं है" योजना टेबल बी पर एक सीक आधारित योजना को प्रोत्साहित करती है। यह एक अच्छा विकल्प है जब टेबल ए छोटा होता है और टेबल बी बड़ा होता है (और बी पर एक सूचकांक मौजूद होता है)।

"एंटीजन" योजना एक अच्छा विकल्प है जब टेबल ए बहुत बड़ा है या टेबल बी बहुत छोटा है या बी पर कोई सूचकांक नहीं है और एक बड़ा परिणाम सेट लौटा रहा है।

हालाँकि यह सिर्फ एक "प्रोत्साहन" है, एक भारित इनपुट की तरह। एक मजबूत (1), (2), (3) अक्सर (4) मूट के लिए विकल्प बनाती है।

(@MaxVernon जवाब द्वारा संबोधित * के कारण विभिन्न कॉलमों को लौटाने वाले अपने उदाहरण के प्रभाव को अनदेखा करना।)।

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