नहीं बनाम में नहीं


538

इनमें से कौन सा प्रश्न अधिक तेज़ है?

अस्तित्व में नहीं है:

SELECT ProductID, ProductName 
FROM Northwind..Products p
WHERE NOT EXISTS (
    SELECT 1 
    FROM Northwind..[Order Details] od 
    WHERE p.ProductId = od.ProductId)

या में नहीं:

SELECT ProductID, ProductName 
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
    SELECT ProductID 
    FROM Northwind..[Order Details])

क्वेरी निष्पादन योजना का कहना है कि वे दोनों एक ही काम करते हैं। यदि ऐसा है, तो अनुशंसित फॉर्म क्या है?

यह नॉर्थविंड डेटाबेस पर आधारित है।

[संपादित करें]

बस यह उपयोगी लेख मिला: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

मुझे लगता है कि मैं नहीं के साथ रहना होगा।


3
क्या आपने योजना बनाई है कि बाईं ओर का उपयोग कर जहां अशक्त है?
सेबास

1
IN IN और NOT EXISTS समान नहीं हैं। उनके बीच अंतर के लिए इस लिंक पर एक नज़र: weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
अमेया गोखले

2
मुझे आश्चर्य है कि डेटाबेस में अंतर है, लेकिन PostgreSQL के खिलाफ मेरे नवीनतम बेंचमार्क में, यह NOT INप्रश्न: SELECT "A".* FROM "A" WHERE "A"."id" NOT IN (SELECT "B"."Aid" FROM "B" WHERE "B"."Uid" = 2)लगभग 30 गुना तेजी से इस के रूप में है NOT EXISTS:SELECT "A".* FROM "A" WHERE (NOT (EXISTS (SELECT 1 FROM "B" WHERE "B"."user_id" = 2 AND "B"."Aid" = "A"."id")))
Nguyễn


1
@rcdmk क्या आपने प्रश्नों पर तारीख की जांच की?
इलीटिरिट

जवाबों:


693

मैं हमेशा डिफॉल्ट करता हूं NOT EXISTS

निष्पादन की योजना इस समय एक ही हो सकता है लेकिन या तो स्तंभ भविष्य में बदल दिया जाता है की अनुमति के लिए अगर NULLरों NOT INसंस्करण अधिक काम करने के (भले ही कोई आवश्यकता होगी NULLरों वास्तव में डेटा में मौजूद हैं) और के शब्दों NOT INअगर NULLरों हैं वर्तमान वैसे भी आप चाहते हैं कि होने की संभावना नहीं है।

जब न तो Products.ProductIDया [Order Details].ProductIDअनुमति देने के NULLरों NOT INनिम्न क्वेरी के समान माना जाएगा।

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

सटीक योजना भिन्न हो सकती है लेकिन मेरे उदाहरण डेटा के लिए मुझे निम्नलिखित मिलेंगे।

न ही NULL

एक आम तौर पर आम गलत धारणा है कि सहसंबद्ध उप-प्रश्न हमेशा जॉइन की तुलना में "खराब" होते हैं। वे निश्चित रूप से तब हो सकते हैं जब वे एक नेस्टेड लूप योजना को बल देते हैं (उप क्वेरी पंक्ति द्वारा पंक्ति का मूल्यांकन किया जाता है) लेकिन इस योजना में एक विरोधी अर्द्ध तार्किक ऑपरेटर शामिल हैं। एंटी सेमी जॉन्स नेस्टेड लूप्स तक सीमित नहीं हैं, लेकिन हैश या मर्ज का उपयोग कर सकते हैं (जैसा कि इस उदाहरण में) भी जुड़ता है।

/*Not valid syntax but better reflects the plan*/ 
SELECT p.ProductID,
       p.ProductName
FROM   Products p
       LEFT ANTI SEMI JOIN [Order Details] od
         ON p.ProductId = od.ProductId 

अगर [Order Details].ProductIDहै NULLसुलभ क्वेरी तो हो जाता है

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL) 

इसका कारण यह है कि यदि [Order Details]कोई भी शब्द है तो सही शब्दार्थ NULL ProductIdकोई परिणाम नहीं है। योजना में जोड़ा गया है यह सत्यापित करने के लिए अतिरिक्त विरोधी अर्ध शामिल होने और पंक्ति गणना स्पूल देखें।

एक नल

यदि क्वेरी को -able Products.ProductIDबनने के लिए भी बदल दिया NULLजाता है तो बन जाता है

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL)
       AND NOT EXISTS (SELECT *
                       FROM   (SELECT TOP 1 *
                               FROM   [Order Details]) S
                       WHERE  p.ProductID IS NULL) 

उस एक का कारण यह है क्योंकि NULL Products.ProductIdपरिणामों में नहीं लौटा जाना चाहिए, सिवाय इसके कि यदि NOT INउप क्वेरी को कोई परिणाम वापस नहीं करना है (यानी [Order Details]तालिका खाली है)। किस मामले में यह होना चाहिए। मेरे नमूना डेटा की योजना में इसे नीचे दिए गए एक और विरोधी अर्ध जोड़कर लागू किया गया है।

दोनों NULL

इसका प्रभाव बकले द्वारा पहले से जुड़े ब्लॉग पोस्ट में दिखाया गया है । उदाहरण में वहाँ तार्किक की संख्या लगभग 400 से 500,000 तक बढ़ जाती है।

इसके अतिरिक्त तथ्य यह है कि एक एकल NULLपंक्ति गणना को शून्य तक कम कर सकता है, कार्डिनैलिटी का अनुमान बहुत मुश्किल करता है। यदि SQL सर्वर मानता है कि ऐसा होगा, लेकिन वास्तव NULLमें डेटा में कोई पंक्तियाँ नहीं थीं, तो बाकी निष्पादन योजना भयावह रूप से बदतर हो सकती है, यदि यह एक बड़ी क्वेरी का सिर्फ एक हिस्सा है, तो अनुचित नेस्टड लूप के कारण एक महंगी उप का बार-बार निष्पादन होता है। उदाहरण के लिए पेड़

हालांकि, NOT INएक-योग्य NULLस्तंभ पर यह एकमात्र संभव निष्पादन योजना नहीं है । यह आलेखAdventureWorks2008 डेटाबेस के विरुद्ध क्वेरी के लिए एक और दिखाता है।

के लिए NOT INएक पर NOT NULLस्तंभ या NOT EXISTSया तो एक नल या गैर व्यर्थ स्तम्भ के खिलाफ यह निम्न योजना देता है।

अस्तित्व में नहीं है

जब-जब योजना में स्तंभ बदलता है NULL, तो NOT INअब जैसा दिखता है

नहीं में - अशक्त

यह योजना में एक अतिरिक्त आंतरिक जुड़ने वाला ऑपरेटर जोड़ता है। यह उपकरण यहाँ समझाया गया है । पिछले एकल सहसंबद्ध सूचकांक की तलाश को Sales.SalesOrderDetail.ProductID = <correlated_product_id>प्रति बाहरी पंक्ति में दो बार करने के लिए यह सब है । अतिरिक्त एक पर है WHERE Sales.SalesOrderDetail.ProductID IS NULL

के रूप में यह एक विरोधी अर्ध में शामिल होने के लिए है कि अगर किसी भी पंक्तियों को वापस ले जाता है तो दूसरी तलाश नहीं होगी। हालाँकि, यदि Sales.SalesOrderDetailइसमें कोई NULL ProductIDs शामिल नहीं है, तो यह आवश्यक आपरेशनों की संख्या को दोगुना कर देगा।


4
क्या मैं पूछ सकता हूं कि आपको दिखाए गए प्रोफाइल की रूपरेखा कैसे मिलती है?
xis

5
@xis ये SQL Sentry plan एक्सप्लोरर में खोले गए एक्जीक्यूटिव प्लान हैं। आप एसएसएमएस में ग्राफिक रूप से निष्पादन योजनाओं को भी देख सकते हैं।
मार्टिन स्मिथ

मैं एकमात्र कारण के लिए इसकी सराहना करता हूं: NOT EXISTSजिस तरह से मैं NOT INकाम करने की उम्मीद करता हूं (जो, यह नहीं है)।
लेविनिन्जा

साथ मौजूद नहीं है, मैं इस तरह के मौजूद नहीं है के रूप में चयन 1 उपयोग करने का प्रयास (से sometable जहां कुछ का चयन करें 1) इतना है कि डेटाबेस वास्तव में डिस्क से कॉलम वापस जाने के लिए की जरूरत नहीं है। यह निर्धारित करने के लिए EXPLAIN का उपयोग करना कि क्या इससे आपके मामले में कोई फर्क पड़ता है, शायद एक अच्छा विचार है।
मयूर पटेल

4
@Mayur को SQL सर्वर में इसकी कोई आवश्यकता नहीं है। stackoverflow.com/questions/1597442/...
मार्टिन स्मिथ

84

यह भी ध्यान रखें कि जब यह शून्य आता है तो IN IN NOT NOT EXISTS के बराबर नहीं है।

यह पोस्ट इसे बहुत अच्छी तरह से समझाती है

http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

जब सबक्वेरी एक नल भी लौटती है, तो IN IN किसी भी पंक्तियों से मेल नहीं खाएगा।

इसका कारण नॉट इन ऑपरेशन वास्तव में क्या है, इसका विवरण देखकर पाया जा सकता है।

मान लीजिए, उदाहरण के प्रयोजनों के लिए कि तालिका में 4 पंक्तियाँ हैं जिन्हें टी कहा जाता है, मान 1..4 के साथ आईडी नामक एक कॉलम है

WHERE SomeValue NOT IN (SELECT AVal FROM t)

के बराबर है

WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1)
AND SomeValue != (SELECT AVal FROM t WHERE ID=2)
AND SomeValue != (SELECT AVal FROM t WHERE ID=3)
AND SomeValue != (SELECT AVal FROM t WHERE ID=4)

आगे बताते हैं कि AVAL NULL है जहाँ ID = 4. इसलिए वह! = तुलना UNKNOWN करता है। AND के लिए तार्किक सत्य तालिका यह बताती है कि UNKNOWN और TRUE UNKNOWN, UNKNOWN और FALSE FALSE है। ऐसा कोई मूल्य नहीं है जो TRUE के उत्पादन के लिए UNKNOWN के साथ AND'd हो सकता है

इसलिए, यदि उस उपश्रेणी की कोई भी पंक्ति NULL को लौटाती है, तो पूरा NOT ऑपरेटर ऑपरेटर या तो FALSE या NULL का मूल्यांकन करेगा और कोई रिकॉर्ड वापस नहीं आएगा


24

यदि निष्पादन योजनाकार कहता है कि वे समान हैं, तो वे समान हैं। जो भी आपके इरादे को और स्पष्ट करेगा - इस मामले में, दूसरा उपयोग करें।


3
निष्पादन योजनाकार समय समान हो सकता है लेकिन निष्पादन परिणाम भिन्न हो सकते हैं इसलिए अंतर है। यदि आप अपने डेटासेट में NULL है तो अनपेक्षित परिणाम न दें (बकले का उत्तर देखें)। डिफ़ॉल्ट के रूप में नहीं EXISTS का उपयोग करने के लिए सबसे अच्छा है।
नानोनर्ड

15

वास्तव में, मेरा मानना ​​है कि यह सबसे तेज़ होगा:

SELECT ProductID, ProductName 
    FROM Northwind..Products p  
          outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null

2
ऑप्टिमाइज़र जब काम कर रहा होता है तो यह सबसे तेज़ नहीं हो सकता है, लेकिन जब यह नहीं होगा तो निश्चित रूप से तेज़ होगा।
केड रूक्स

2
उन्होंने इस पोस्ट के लिए अपनी क्वेरी को भी सरल बनाया हो सकता है
किप

1
सहमत लेफ्ट एक्सटर्नल ज्वाइनिंग अक्सर सब-वे की तुलना में तेज होती है।
HLGEM

7
@HLGEM असहमत। मेरे अनुभव में एलओजे के लिए सबसे अच्छा मामला यह है कि वे समान हैं और एसक्यूएल सर्वर एलओजे को एक विरोधी अर्ध सम्मिलित में परिवर्तित करता है। सबसे खराब स्थिति में SQL सर्वर LEFT सब कुछ शामिल करता है और NULLs को फ़िल्टर करता है जिसके बाद अधिक अक्षम हो सकता है। इस लेख के नीचे इसका उदाहरण
मार्टिन स्मिथ

12

मेरे पास एक तालिका है जिसमें लगभग 120,000 रिकॉर्ड हैं और केवल उन लोगों का चयन करने की आवश्यकता है जो मौजूद नहीं हैं (एक varchar स्तंभ के साथ मेल खाता है) चार अन्य तालिकाओं में पंक्तियों की संख्या लगभग 1500, 4000, 40000, 200 है। सभी शामिल तालिकाओं में अद्वितीय सूचकांक हैं संबंधित Varcharकॉलम पर।

NOT IN लगभग 10 मिनट लगे, NOT EXISTS लिया 4 सेकंड लिया।

मेरे पास एक पुनरावर्ती प्रश्न है, जिसमें कुछ अनकहा अनुभाग हो सकता है, जिसने 10 मिनट तक योगदान दिया हो, लेकिन 4 सेकंड लेने वाले अन्य विकल्प बताते हैं, कम से कम मेरे लिए यह NOT EXISTSबेहतर है या कम से कम ऐसा है INऔर EXISTSबिल्कुल वैसा ही नहीं है और हमेशा लायक है कोड के साथ आगे जाने से पहले जाँच करें।


8

आपके विशिष्ट उदाहरण में वे समान हैं, क्योंकि आशावादी ने यह पता लगा लिया है कि आप जो करने की कोशिश कर रहे हैं, वह दोनों उदाहरणों में समान है। लेकिन यह संभव है कि गैर-तुच्छ उदाहरणों में आशावादी ऐसा नहीं कर सकता है, और उस स्थिति में अवसर पर एक से दूसरे को पसंद करने के कारण हैं।

NOT INयदि आप अपने बाहरी चयन में कई पंक्तियों का परीक्षण कर रहे हैं, तो इसे प्राथमिकता दी जानी चाहिए। NOT INस्टेटमेंट के अंदर सबक्वेरी का मूल्यांकन निष्पादन की शुरुआत में किया जा सकता है, और अस्थायी तालिका को बाहरी चयन में प्रत्येक मूल्य के खिलाफ जांचा जा सकता है, बजाय हर बार सबसेंप्ट को फिर से चलाने के बजाय, जैसा कि आवश्यक होगाNOT EXISTS स्टेटमेंट के ।

यदि उपश्रेणी को बाहरी चयन के साथ सहसंबद्ध होना चाहिए, तो NOT EXISTSयह बेहतर हो सकता है, क्योंकि ऑप्टिमाइज़र एक सरलीकरण की खोज कर सकता है जो समान कार्य करने के लिए किसी भी अस्थायी तालिकाओं के निर्माण को रोकता है।


6

मैं उपयोग कर रहा था

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)

और पाया कि यह गलत परिणाम दे रहा था (गलत से मेरा मतलब है कोई परिणाम नहीं)। जैसा कि TABLE2.Col1 में एक NULL था।

क्वेरी को बदलते समय

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)

मुझे सही परिणाम दिए।

तब से मैंने हर जगह NOT EXISTS का उपयोग शुरू कर दिया है।


5

वे बहुत समान हैं लेकिन वास्तव में समान नहीं हैं।

कार्यकुशलता के संदर्भ में, मैंने पाया है कि बायां जुड़ाव अशक्त कथन अधिक कुशल है (जब बहुतायत में पंक्तियों का चयन किया जाना है)


2

यदि आशावादी कहते हैं कि वे समान हैं तो मानव कारक पर विचार करें। मैं देखना पसंद नहीं :)


1

यह एक बहुत अच्छा सवाल है, इसलिए मैंने अपने ब्लॉग पर इस विषय के बारे में एक बहुत विस्तृत लेख लिखने का फैसला किया है ।

डेटाबेस तालिका मॉडल

मान लेते हैं कि हमारे डेटाबेस में निम्नलिखित दो टेबल हैं, जो एक-से-कई टेबल संबंध बनाते हैं।

SQL EXISTS टेबल

studentतालिका अभिभावक होता है औरstudent_grade बच्चे को मेज है, क्योंकि यह एक student_id विदेशी कुंजी स्तंभ छात्र तालिका में आईडी प्राथमिक कुंजी स्तंभ को संदर्भित है।

student tableनिम्नलिखित दो रिकॉर्ड में शामिल हैं:

| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1  | Alice      | Smith     | 8.95            |
| 2  | Bob        | Johnson   | 8.75            |

और, student_gradeतालिका उन ग्रेडों को संग्रहीत करती है जो छात्र प्राप्त करते हैं:

| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1  | Math       | 10    | 1          |
| 2  | Math       | 9.5   | 1          |
| 3  | Math       | 9.75  | 1          |
| 4  | Science    | 9.5   | 1          |
| 5  | Science    | 9     | 1          |
| 6  | Science    | 9.25  | 1          |
| 7  | Math       | 8.5   | 2          |
| 8  | Math       | 9.5   | 2          |
| 9  | Math       | 9     | 2          |
| 10 | Science    | 10    | 2          |
| 11 | Science    | 9.4   | 2          |

SQL EXISTS

मान लीजिए कि हम उन सभी छात्रों को प्राप्त करना चाहते हैं जिन्हें गणित कक्षा में 10 ग्रेड प्राप्त हुए हैं।

यदि हम केवल छात्र पहचानकर्ता में रुचि रखते हैं, तो हम इस तरह एक क्वेरी चला सकते हैं:

SELECT
    student_grade.student_id
FROM
    student_grade
WHERE
    student_grade.grade = 10 AND
    student_grade.class_name = 'Math'
ORDER BY
    student_grade.student_id

लेकिन, आवेदन studentकेवल पहचानकर्ता का पूरा नाम प्रदर्शित करने में रुचि रखता है , इसलिए हमें इसकी जानकारी चाहिएstudent तालिका ।

studentगणित में 10 ग्रेड वाले रिकॉर्ड को फ़िल्टर करने के लिए , हम इस तरह से EXISTS SQL ऑपरेटर का उपयोग कर सकते हैं:

SELECT
    id, first_name, last_name
FROM
    student
WHERE EXISTS (
    SELECT 1
    FROM
        student_grade
    WHERE
        student_grade.student_id = student.id AND
        student_grade.grade = 10 AND
        student_grade.class_name = 'Math'
)
ORDER BY id

ऊपर क्वेरी चलाते समय, हम देख सकते हैं कि केवल ऐलिस पंक्ति चयनित है:

| id | first_name | last_name |
|----|------------|-----------|
| 1  | Alice      | Smith     |

बाहरी क्वेरी उस studentपंक्ति कॉलम का चयन करती है जिसे हम ग्राहक को वापस करने में रुचि रखते हैं। हालाँकि, WHERE क्लॉज संबंधित आंतरिक उपकुंजी के साथ EXISTS ऑपरेटर का उपयोग कर रहा है।

यदि किसी पंक्ति का चयन नहीं किया जाता है, तो उप-परिचालक कम से कम एक रिकॉर्ड और गलत रिटर्न देता है, तो सही है। डेटाबेस इंजन को पूरी तरह से उपकुंजी चलाने की आवश्यकता नहीं है। यदि एक एकल रिकॉर्ड का मिलान किया जाता है, तो EXISTS ऑपरेटर सही हो जाता है, और संबंधित अन्य क्वेरी पंक्ति चयनित होती है।

इनर सबक्वेरी को सहसंबंधित किया जाता है क्योंकि छात्र_एड कॉलम student_grade तालिका id कॉलम को बाहरी छात्र तालिका के आईडी कॉलम से मिलान किया जाता है।

SQL EXISTS नहीं है

आइए विचार करें कि हम उन सभी छात्रों का चयन करना चाहते हैं जिनके पास ग्रेड 9 से कम नहीं है। इसके लिए हम NOT EXISTS का उपयोग कर सकते हैं, जो EXISTS ऑपरेटर के तर्क को नकारता है।

इसलिए, अंतर्निहित सबस्क्रिप्शन कोई रिकॉर्ड नहीं है, तो नहीं EXISTS ऑपरेटर सही देता है। हालाँकि, यदि किसी एकल रिकॉर्ड को आंतरिक सबक्वेरी द्वारा मिलान किया जाता है, तो NOT EXISTS ऑपरेटर गलत नहीं लौटेगा, और सबक्वेरी निष्पादन को रोका जा सकता है।

उन सभी छात्र रिकॉर्ड्स से मिलान करने के लिए, जिनका 9 से कम मान वाला कोई संबद्ध छात्र_ग्रेड नहीं है, हम निम्नलिखित SQL क्वेरी चला सकते हैं:

SELECT
    id, first_name, last_name
FROM
    student
WHERE NOT EXISTS (
    SELECT 1
    FROM
        student_grade
    WHERE
        student_grade.student_id = student.id AND
        student_grade.grade < 9
)
ORDER BY id

ऊपर क्वेरी चलाते समय, हम देख सकते हैं कि केवल ऐलिस रिकॉर्ड का मिलान किया गया है:

| id | first_name | last_name |
|----|------------|-----------|
| 1  | Alice      | Smith     |

तो, SQL EXISTS और NOT EXISTS ऑपरेटरों का उपयोग करने का लाभ यह है कि जब तक एक मिलान रिकॉर्ड पाया जाता है तब तक आंतरिक सबक्वेरी निष्पादन को रोका जा सकता है।


-1

निर्भर करता है..

SELECT x.col
FROM big_table x
WHERE x.key IN( SELECT key FROM really_big_table );

अपेक्षाकृत धीमी गति से सीमित नहीं किया जा सकता है, यह देखने के लिए कि उनके पास कुंजी क्या है, क्वेरी के आकार को सीमित करने के लिए बहुत ज्यादा नहीं है। इस मामले में EXISTS बेहतर होगा।

लेकिन, DBMS के ऑप्टिमाइज़र के आधार पर, यह अलग नहीं हो सकता है।

जब EXISTS बेहतर हो तो उदाहरण के रूप में

SELECT x.col
FROM big_table x
WHERE EXISTS( SELECT key FROM really_big_table WHERE key = x.key);
  AND id = very_limiting_criteria

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