यह क्वेरी, FROM क्लॉज़ को याद नहीं कर रही है, त्रुटि नहीं है?


9

इसलिए हमारे पास एक उपश्रेणी के साथ एक क्वेरी है जिसमें एक टाइपो है। यह FROM क्लॉज को याद कर रहा है। लेकिन जब आप इसे चलाते हैं, तो यह त्रुटि नहीं करता है! क्यों!?


SELECT

    1
   ,r.id
   ,'0D4133BE-C1B5-4141-AFAD-B171A2CCCE56'
   ,GETDATE()
   ,1
   ,'Y'
   ,'N'
   ,oldItem.can_view
   ,oldItem.can_update

FROM Role r

JOIN RoleObject oldReport
    ON r.customer_id = oldReport.customer_id

JOIN RoleItem oldItem
    ON oldReport.id = oldItem.role_object_id
        AND r.id = oldItem.role_id

WHERE r.id NOT IN (SELECT
        role_id
    WHERE role_object_id = '0D4133BE-C1B5-4141-AFAD-B171A2CCCE56')

AND oldReport.id = '169BA22F-1614-4EBA-AF45-18E333C54C6C'

जवाबों:


21

यह कथन कानूनी है (दूसरे शब्दों में, FROMआवश्यक नहीं है):

SELECT x = 1;
SELECT x = 1 WHERE 1 = 1; -- also try WHERE 1 = 0;

जब आप स्पष्ट रूप से मौजूद नहीं हो सकते हैं, तो एक कॉलम नाम पेश करते समय चाल। तो ये विफल:

SELECT name WHERE 1 = 1;

SELECT x = 1 WHERE id > 0;

Msg 207, स्तर 16, राज्य 1
अमान्य स्तंभ नाम 'नाम'।
Msg 207, स्तर 16, राज्य 1
अमान्य स्तंभ नाम 'id'।

लेकिन जब अवैध कॉलम को किसी सबक्वेरी की तरह पेश किया जाता है, तो SQL सर्वर तब क्या करता है जब वह उस कॉलम को सबक्वेरी के आंतरिक दायरे में नहीं पाता है, बाहरी दायरे में ट्रावर्स हो जाता है, और सबक्वेरी को उस बाहरी दायरे से संबंधित बना देता है। यह सभी पंक्तियों को लौटाएगा, उदाहरण के लिए:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE 1 = 1);

क्योंकि यह अनिवार्य रूप से कह रहा है:

SELECT * FROM sys.columns WHERE name IN (SELECT sys.columns.name WHERE 1 = 1); /*
              ^^^^^^^^^^^                       -----------
                   |                                 |
                   -----------------------------------    */

आपको WHEREउपनगर में एक खंड की भी आवश्यकता नहीं है :

SELECT * FROM sys.columns WHERE name IN (SELECT name);

आप देख सकते हैं कि यह वास्तव में बाहरी स्कॉप्ड टेबल को देख रहा है, क्योंकि यह:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE name > N'x');

बहुत कम पंक्तियों को लौटाता है (मेरे सिस्टम पर 11)।

इसमें स्कूपिंग के बारे में मानक का पालन शामिल है। जब आप दो # टेबल टेबल रखते हैं तो आप ऐसी ही चीजें देख सकते हैं:

CREATE TABLE #foo(foo int);
CREATE TABLE #bar(bar int);

SELECT foo FROM #foo WHERE foo IN (SELECT foo FROM #bar);

जाहिर है, यह त्रुटि होनी चाहिए, ठीक है, क्योंकि इसमें कोई भी नहीं fooहै #bar? नहीं। क्या होता है कि SQL सर्वर कहता है, "ओह, मुझे fooयहां नहीं मिला , आपको दूसरे का मतलब होना चाहिए।"

इसके अलावा, सामान्य तौर पर, मैं इससे बचूंगा NOT INNOT EXISTSकुछ परिदृश्यों में अधिक कुशल होने की क्षमता है, लेकिन इससे भी महत्वपूर्ण बात यह है कि इसका व्यवहार तब नहीं बदलता है जब यह संभव होता है कि लक्ष्य स्तंभ हो सकता है NULLअधिक जानकारी के लिए इस पोस्ट को देखें


मैंने स्टैक ओवरफ्लो पर एक प्रश्न पूछा, जिसका उत्तर अनिवार्य रूप से इस तरह है (हालांकि आपका अधिक गहन है)। कॉलम (बाएं हाथ के ऑपरेंड के रूप में) का संदर्भ क्यों दिया जा रहा है, जो EXISTS ऑपरेटर में त्रुटि नहीं होने के कारण सारणी का हिस्सा नहीं है?
मार्क.2377

2

मैंने इसे 2016 में सरलीकृत उदाहरण के साथ पुन: प्रस्तुत किया:

declare @t1 table (c1 int, c2 int, c3 int)
insert into @t1 values (1,2,3), (2,3,4), (3,4,5)

select * from @t1
where
    c1 not in 
    (select c2 where c3 = 3)

ऐसा प्रतीत होता है कि प्रत्येक पंक्ति के लिए c2 और c3 का मूल्यांकन किया जाता है।


1

SQL सर्वर सिलेक्ट सिंटैक्स में FROM सेक्शन की आवश्यकता नहीं होती है। यदि आप FROM को छोड़ देते हैं, तो चयन विवरण "डमी" तालिका का उपयोग करेगा जिसमें एक पंक्ति और कोई कॉलम नहीं है। इसलिए

select 'x' as c where ...

यदि अभिव्यक्ति सत्य है और गलत होने पर कोई पंक्तियाँ वापस नहीं आएंगी


लेकिन यह काम नहीं करता है यदि आप सिर्फ कहते हैं select cऔर cकिसी बाहरी वस्तु में मौजूद नहीं है। मैं मानता हूँ कि FROMआवश्यकता नहीं है, लेकिन जब आप स्पष्ट रूप से एक स्तंभ नाम यहाँ खेलने में यांत्रिकी करता है एक बाहरी दायरे में अस्तित्व निश्चित रूप से एक डमी तालिका से अलग हैं, और यदि आप एक स्तंभ जो ऐसा नहीं करता के लिए एक निरंतर प्रदान नहीं करते मौजूद है, आपको एक रनटाइम त्रुटि मिलती है, इसलिए वहां कोई डमी टेबल नहीं है। डमी टेबल अन्य परिदृश्यों में खेलने के लिए आ सकती है, लेकिन तब नहीं जब संदर्भ उप-तालिका / व्युत्पन्न तालिका में हो।
हारून बर्ट्रेंड

आपके उदाहरण में यह एक सह-संबद्ध उप-चयन है, role_id और role_object_id बाहरी चयन में तालिकाओं में से एक से संबंधित है।
पियोट्र

ठीक है, लेकिन यह कहना SELECT 'x' AS cओपी की तुलना में पूरी तरह से अलग परिदृश्य है, जिसने अभी कहा SELECT cएक सबक्वेरी / व्युत्पन्न तालिका में।
हारून बर्ट्रेंड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.