यह एक ऐसा मुद्दा है जो मैं समय-समय पर उठाता हूं और अभी तक इसका कोई अच्छा समाधान नहीं खोज पाया हूं।
निम्न तालिका संरचना का समर्थन करना
CREATE TABLE T
(
A INT PRIMARY KEY,
B CHAR(1000) NULL,
C CHAR(1000) NULL
)
और आवश्यकता यह निर्धारित करने के लिए है कि क्या या तो अशक्त स्तंभ हैं Bया Cवास्तव में कोई भी NULLमान हैं (और यदि ऐसा है तो कौन है)।
यह भी मान लें कि तालिका में लाखों पंक्तियाँ हैं (और यह कि कोई भी स्तंभ आँकड़े उपलब्ध नहीं हैं, जिन्हें मैं इस प्रश्न के वर्ग के लिए एक अधिक सामान्य समाधान में दिलचस्पी के रूप में देखा जा सकता है)।
मैं इसके निकट आने के कुछ तरीकों के बारे में सोच सकता हूं लेकिन सभी में कमजोरियां हैं।
दो अलग-अलग EXISTSबयान। इससे यह लाभ होगा कि जैसे ही यह पता चलता है, प्रश्नों को स्कैन करने से रोकने की अनुमति NULLमिलती है। लेकिन अगर वास्तव में दोनों कॉलम में कोई NULLएस नहीं है तो दो पूर्ण स्कैन का परिणाम होगा।
सिंगल एग्रीगेट क्वेरी
SELECT
MAX(CASE WHEN B IS NULL THEN 1 ELSE 0 END) AS B,
MAX(CASE WHEN C IS NULL THEN 1 ELSE 0 END) AS C
FROM T
यह एक ही समय में दोनों स्तंभों को संसाधित कर सकता है इसलिए एक पूर्ण स्कैन का सबसे खराब मामला है। नुकसान यह है कि भले ही यह NULLक्वेरी पर बहुत जल्दी दोनों स्तंभों में सामना करता है , फिर भी तालिका के बाकी हिस्सों को स्कैन करना समाप्त कर देगा।
उपयोगकर्ता चर
मैं ऐसा करने के तीसरे तरीके के बारे में सोच सकता हूं
BEGIN TRY
DECLARE @B INT, @C INT, @D INT
SELECT
@B = CASE WHEN B IS NULL THEN 1 ELSE @B END,
@C = CASE WHEN C IS NULL THEN 1 ELSE @C END,
/*Divide by zero error if both @B and @C are 1.
Might happen next row as no guarantee of order of
assignments*/
@D = 1 / (2 - (@B + @C))
FROM T
OPTION (MAXDOP 1)
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 8134 /*Divide by zero*/
BEGIN
SELECT 'B,C both contain NULLs'
RETURN;
END
ELSE
RETURN;
END CATCH
SELECT ISNULL(@B,0),
ISNULL(@C,0)
लेकिन यह उत्पादन कोड के लिए उपयुक्त नहीं है क्योंकि समुच्चयबोधक क्वेरी के लिए सही व्यवहार अपरिभाषित है। और एक त्रुटि को फेंककर स्कैन को समाप्त करना वैसे भी काफी भयानक समाधान है।
क्या कोई अन्य विकल्प है जो ऊपर दिए गए दृष्टिकोणों की ताकत को जोड़ता है?
संपादित करें
अब तक प्रस्तुत किए गए उत्तरों के संदर्भ में मेरे द्वारा प्राप्त परिणामों के साथ इसे अपडेट करने के लिए (@ ypercube के परीक्षण डेटा का उपयोग करके)
+----------+------------+------+---------+----------+----------------------+----------+------------------+
| | 2 * EXISTS | CASE | Kejser | Kejser | Kejser | ypercube | 8kb |
+----------+------------+------+---------+----------+----------------------+----------+------------------+
| | | | | MAXDOP 1 | HASH GROUP, MAXDOP 1 | | |
| No Nulls | 15208 | 7604 | 8343 | 7604 | 7604 | 15208 | 8346 (8343+3) |
| One Null | 7613 | 7604 | 8343 | 7604 | 7604 | 7620 | 7630 (25+7602+3) |
| Two Null | 23 | 7604 | 8343 | 7604 | 7604 | 30 | 30 (18+12) |
+----------+------------+------+---------+----------+----------------------+----------+------------------+
@ थॉमस का जवाब मैं बदल के लिए TOP 3करने के लिए TOP 2यह पहले बाहर निकलने के लिए संभावित रूप से अनुमति देने के लिए। मुझे उस उत्तर के लिए डिफ़ॉल्ट रूप से एक समानांतर योजना मिली, इसलिए MAXDOP 1अन्य योजनाओं की तुलना में अधिक पढ़े जाने की संख्या को सुनिश्चित करने के लिए इसे एक संकेत के साथ आजमाया । मैं परिणामों से कुछ हद तक आश्चर्यचकित था क्योंकि मेरे पहले के परीक्षण में मैंने पूरी तालिका को पढ़े बिना उस शार्ट सर्किट को देखा था।
मेरे परीक्षण डेटा की योजना है कि शॉर्ट सर्किट नीचे है

Ypercube के डेटा की योजना है

तो यह योजना के लिए एक अवरुद्ध प्रकार ऑपरेटर जोड़ता है। मैंने भी HASH GROUPसंकेत के साथ प्रयास किया लेकिन वह अभी भी सभी पंक्तियों को पढ़ते हुए समाप्त होता है

ऐसा लगता है कि कुंजी को hash match (flow distinct)इस योजना को शॉर्ट सर्किट की अनुमति देने के लिए एक ऑपरेटर मिल रहा है क्योंकि अन्य विकल्प वैसे भी सभी पंक्तियों को अवरुद्ध और उपभोग करेंगे। मुझे नहीं लगता कि यह विशेष रूप से लेकिन जाहिरा तौर पर मजबूर करने के लिए संकेत है , "सामान्य तौर पर, ऑप्टिमाइज़र एक फ्लो डिस्टिक्ट चुनता है जहां यह निर्धारित करता है कि इनपुट सेट में अलग-अलग मान होने से कम आउटपुट पंक्तियों की आवश्यकता होती है।" ।
@ ypercube के डेटा में NULLमूल्यों के साथ प्रत्येक कॉलम में केवल 1 पंक्ति है (तालिका कार्डिनैलिटी = 30300) और ऑपरेटर के बाहर और बाहर जाने वाली अनुमानित पंक्तियाँ दोनों हैं 1। आशावादी को विधेय को थोड़ा और अधिक अपारदर्शी बनाकर उसने फ्लो डिस्टिक्ट ऑपरेटर के साथ एक योजना तैयार की।
SELECT TOP 2 *
FROM (SELECT DISTINCT
CASE WHEN b IS NULL THEN NULL ELSE 'foo' END AS b
, CASE WHEN c IS NULL THEN NULL ELSE 'bar' END AS c
FROM test T
WHERE LEFT(b,1) + LEFT(c,1) IS NULL
) AS DT
संपादित करें 2
एक आखिरी मोड़ जो मेरे साथ हुआ, वह यह है कि ऊपर की क्वेरी अभी भी उस घटना में आवश्यक से अधिक पंक्तियों को संसाधित करने में समाप्त हो सकती है जो पहली पंक्ति में एक NULLस्तंभ में NULLs के साथ सामना करती है Bऔर C। यह तुरंत बाहर निकलने के बजाय स्कैनिंग जारी रखेगा। इससे बचने का एक तरीका यह है कि पंक्तियों को स्कैन करने के बाद उन्हें खोल दिया जाए। इसलिए थॉमस केसर के जवाब में मेरा अंतिम संशोधन नीचे है
SELECT DISTINCT TOP 2 NullExists
FROM test T
CROSS APPLY (VALUES(CASE WHEN b IS NULL THEN 'b' END),
(CASE WHEN c IS NULL THEN 'c' END)) V(NullExists)
WHERE NullExists IS NOT NULL
यह संभव है कि विधेय के लिए बेहतर होगा, WHERE (b IS NULL OR c IS NULL) AND NullExists IS NOT NULLलेकिन पिछले परीक्षण डेटा के खिलाफ कि कोई मुझे फ्लो डिस्टिक्ट के साथ योजना नहीं देता है, जबकि NullExists IS NOT NULLएक (नीचे योजना) करता है।

TOP 3बस हो सकता हैTOP 2जब तक यह निम्न में से प्रत्येक से एक पाता है यह स्कैन करेगा वर्तमान में के रूप में(NOT_NULL,NULL),(NULL,NOT_NULL),(NULL,NULL)। उन 3 में से कोई भी 2 पर्याप्त होगा - और यदि यह(NULL,NULL)पहले मिल जाता है तो दूसरे की भी जरूरत नहीं होगी। इसके अलावा शॉर्ट सर्किट के लिए योजना को एकhash match (flow distinct)ऑपरेटर के बजाय विशिष्ट को लागू करने की आवश्यकता होगीhash match (aggregate)याdistinct sort