Database
 sql >> डेटाबेस >  >> RDS >> Database

पूर्ण जटिलताएं - भाग 2

यह आलेख NULL जटिलताओं पर श्रृंखला में दूसरा है। पिछले महीने मैंने किसी भी प्रकार के लापता मूल्य के लिए NULL को SQL के मार्कर के रूप में पेश किया। मैंने समझाया कि SQL आपको अनुपलब्ध और लागू के बीच अंतर करने की क्षमता प्रदान नहीं करता है (ए-मान) और अनुपलब्ध और अनुपयुक्त (आई-वैल्यू) मार्कर। मैंने यह भी बताया कि एनयूएलएल से जुड़ी तुलना स्थिरांक, चर, पैरामीटर और कॉलम के साथ कैसे काम करती है। इस महीने मैं अलग-अलग टी-एसक्यूएल तत्वों में NULL उपचार विसंगतियों को कवर करके चर्चा जारी रखता हूं।

मैं अपने कुछ उदाहरणों में पिछले महीने की तरह नमूना डेटाबेस TSQLV5 का उपयोग करना जारी रखूंगा। आप इस डेटाबेस को बनाने और भरने वाली स्क्रिप्ट यहां और इसके ईआर आरेख यहां पा सकते हैं।

उपचार की पूर्ण विसंगतियां

जैसा कि आप पहले ही इकट्ठा कर चुके हैं, NULL उपचार तुच्छ नहीं है। कुछ भ्रम और जटिलता इस तथ्य के साथ है कि समान संचालन के लिए टी-एसक्यूएल के विभिन्न तत्वों के बीच एनयूएलएल का उपचार असंगत हो सकता है। आगामी अनुभागों में मैं रैखिक बनाम कुल संगणना, ON/WHERE/HAVING क्लॉज, CHECK बाधा बनाम CHECK विकल्प, IF/WHILE/CASE एलिमेंट्स, MERGE स्टेटमेंट, विशिष्टता और ग्रुपिंग, साथ ही ऑर्डरिंग और विशिष्टता में NULL हैंडलिंग का वर्णन करता हूं।

रैखिक बनाम समग्र गणना

टी-एसक्यूएल, और वही मानक एसक्यूएल के लिए जाता है, कॉलम में एक रैखिक एक के रूप में एक ही गणना को लागू करते समय पंक्तियों में एसयूएम, मिन और मैक्स जैसे वास्तविक कुल फ़ंक्शन को लागू करते समय अलग-अलग न्यूल हैंडलिंग तर्क का उपयोग करता है। इस अंतर को प्रदर्शित करने के लिए, मैं #T1 और #T2 नामक दो नमूना तालिकाओं का उपयोग करूंगा, जिन्हें आप निम्न कोड चलाकर बनाते हैं और पॉप्युलेट करते हैं:

DROP TABLE IF EXISTS #T1, #T2;
 
SELECT * INTO #T1 FROM ( VALUES(10, 5, NULL) ) AS D(col1, col2, col3);
 
SELECT * INTO #T2 FROM ( VALUES(10),(5),(NULL) ) AS D(col1);

तालिका #T1 में तीन कॉलम हैं जिन्हें col1, col2 और col3 कहा जाता है। वर्तमान में इसकी एक पंक्ति है जिसमें कॉलम मान क्रमशः 10, 5 और NULL हैं:

SELECT * FROM #T1;
col1        col2        col3
----------- ----------- -----------
10          5           NULL

तालिका #T2 में एक कॉलम है जिसे col1 कहा जाता है। यह वर्तमान में col1 में मान 10, 5 और NULL के साथ तीन पंक्तियाँ हैं:

SELECT * FROM #T2;
col1
-----------
10
5
NULL

अंततः एक समग्र गणना को लागू करते समय जैसे कि स्तंभों में एक रैखिक एक के रूप में, किसी भी NULL इनपुट की उपस्थिति एक NULL परिणाम उत्पन्न करती है। निम्न क्वेरी इस व्यवहार को प्रदर्शित करती है:

SELECT col1 + col2 + col3 AS total
FROM #T1;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

total
-----------
NULL

इसके विपरीत, वास्तविक कुल कार्य, जो पंक्तियों में लागू होते हैं, NULL इनपुट को अनदेखा करने के लिए डिज़ाइन किए गए हैं। निम्न क्वेरी SUM फ़ंक्शन का उपयोग करके इस व्यवहार को प्रदर्शित करती है:

SELECT SUM(col1) AS total
FROM #T2;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

total
-----------
15

Warning: Null value is eliminated by an aggregate or other SET operation.

SQL मानक द्वारा अनिवार्य चेतावनी पर ध्यान दें जो NULL इनपुट की उपस्थिति को इंगित करता है जिसे अनदेखा कर दिया गया था। आप ANSI_WARNINGS सत्र विकल्प को बंद करके ऐसी चेतावनियों को दबा सकते हैं।

इसी तरह, जब एक इनपुट एक्सप्रेशन पर लागू किया जाता है, तो COUNT फ़ंक्शन गैर-नल इनपुट मानों वाली पंक्तियों की संख्या की गणना करता है (जैसा कि COUNT (*) के विपरीत है जो केवल पंक्तियों की संख्या की गणना करता है)। उदाहरण के लिए, उपरोक्त क्वेरी में SUM(col1) को COUNT(col1) से बदलने पर 2 की गिनती वापस आती है।

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

इस तर्क के आधार पर, AVG फ़ंक्शन गैर-NULL मानों के योग को गैर-NULL मानों की संख्या से विभाजित करता है। एक उदाहरण के रूप में निम्नलिखित प्रश्न पर विचार करें:

SELECT AVG(1.0 * col1) AS avgall
FROM #T2;

यहां गैर-शून्य col1 मानों का योग 15 को गैर-शून्य मानों की संख्या से विभाजित किया जाता है। आप col1 को संख्यात्मक शाब्दिक 1.0 से गुणा करते हैं ताकि पूर्णांक इनपुट मानों को संख्यात्मक विभाजन प्राप्त करने के लिए संख्यात्मक रूप से परिवर्तित किया जा सके और पूर्णांक नहीं। विभाजन। यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

avgall
---------
7.500000

इसी तरह, MIN और MAX समुच्चय NULL इनपुट को अनदेखा करते हैं। निम्नलिखित प्रश्न पर विचार करें:

SELECT MIN(col1) AS mincol1, MAX(col1) AS maxcol1
FROM #T2;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

mincol1     maxcol1
----------- -----------
5           10

रैखिक गणनाओं को लागू करने का प्रयास करना, लेकिन कुल फ़ंक्शन सेमेन्टिक्स का अनुकरण करना (एनयूएलएल को अनदेखा करना) सुंदर नहीं है। SUM, COUNT और AVG का अनुकरण करना बहुत जटिल नहीं है, लेकिन इसके लिए आपको NULLs के लिए प्रत्येक इनपुट की जाँच करनी होगी, जैसे:

SELECT col1, col2, col3,
  CASE
    WHEN COALESCE(col1, col2, col3) IS NULL THEN NULL
    ELSE COALESCE(col1, 0) + COALESCE(col2, 0) + COALESCE(col3, 0)
  END AS sumall,
  CASE WHEN col1 IS NOT NULL THEN 1 ELSE 0 END
    + CASE WHEN col2 IS NOT NULL THEN 1 ELSE 0 END
    + CASE WHEN col3 IS NOT NULL THEN 1 ELSE 0 END AS cntall,
  CASE
    WHEN COALESCE(col1, col2, col3) IS NULL THEN NULL
    ELSE 1.0 * (COALESCE(col1, 0) + COALESCE(col2, 0) + COALESCE(col3, 0))
           / (CASE WHEN col1 IS NOT NULL THEN 1 ELSE 0 END
                + CASE WHEN col2 IS NOT NULL THEN 1 ELSE 0 END
                + CASE WHEN col3 IS NOT NULL THEN 1 ELSE 0 END)
  END AS avgall
FROM #T1;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

col1        col2        col3        sumall      cntall      avgall
----------- ----------- ----------- ----------- ----------- ---------------
10          5           NULL        15          2           7.500000000000

दो से अधिक इनपुट कॉलम के लिए एक रैखिक गणना के रूप में न्यूनतम या अधिकतम लागू करने का प्रयास करना आपके द्वारा NULLs को अनदेखा करने के लिए तर्क जोड़ने से पहले काफी मुश्किल है क्योंकि इसमें प्रत्यक्ष या अप्रत्यक्ष रूप से कई CASE अभिव्यक्तियों को नेस्टिंग करना शामिल है (जब आप कॉलम उपनामों का पुन:उपयोग करते हैं)। उदाहरण के लिए, यहां एक क्वेरी है जो #T1 में col1, col2 और col3 के बीच अधिकतम गणना कर रही है, बिना उस हिस्से के जो NULLs को अनदेखा करता है:

SELECT col1, col2, col3, 
  CASE WHEN col1 IS NULL OR col2 IS NULL OR col3 IS NULL THEN NULL ELSE max2 END AS maxall
FROM #T1
  CROSS APPLY (VALUES(CASE WHEN col1 >= col2 THEN col1 ELSE col2 END)) AS A1(max1)
  CROSS APPLY (VALUES(CASE WHEN max1 >= col3 THEN max1 ELSE col3 END)) AS A2(max2);

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

col1        col2        col3        maxall
----------- ----------- ----------- -----------
10          5           NULL        NULL

यदि आप क्वेरी योजना की जांच करते हैं, तो आप अंतिम परिणाम की गणना करते हुए निम्नलिखित विस्तारित अभिव्यक्ति पाएंगे:

[Expr1005] = Scalar Operator(CASE WHEN CASE WHEN [#T1].[col1] IS NOT NULL THEN [#T1].[col1] ELSE 
  CASE WHEN [#T1].[col2] IS NOT NULL THEN [#T1].[col2] 
    ELSE [#T1].[col3] END END IS NULL THEN NULL ELSE 
  CASE WHEN CASE WHEN [#T1].[col1]>=[#T1].[col2] THEN [#T1].[col1] 
    ELSE [#T1].[col2] END>=[#T1].[col3] THEN 
  CASE WHEN [#T1].[col1]>=[#T1].[col2] THEN [#T1].[col1] 
    ELSE [#T1].[col2] END ELSE [#T1].[col3] END END)

और वह तब होता है जब केवल तीन कॉलम शामिल होते हैं। कल्पना कीजिए कि एक दर्जन कॉलम शामिल हैं!

अब इसमें NULLs को अनदेखा करने का तर्क जोड़ें:

SELECT col1, col2, col3, max2 AS maxall
FROM #T1
  CROSS APPLY (VALUES(CASE WHEN col1 >= col2 OR col2 IS NULL THEN col1 ELSE col2 END)) AS A1(max1)
  CROSS APPLY (VALUES(CASE WHEN max1 >= col3 OR col3 IS NULL THEN max1 ELSE col3 END)) AS A2(max2);

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

col1        col2        col3        maxall
----------- ----------- ----------- -----------
10          5           NULL        10

Oracle में GREATEST और LEAST नामक कार्यों की एक जोड़ी है जो इनपुट मानों के लिए रैखिक वाले के रूप में क्रमशः न्यूनतम और अधिकतम गणना लागू करती है। ये फ़ंक्शन किसी भी NULL इनपुट को दिए गए NULL को अधिकांश रैखिक गणनाओं की तरह लौटाते हैं। टी-एसक्यूएल में समान कार्यों को प्राप्त करने के लिए एक खुला फीडबैक आइटम था, लेकिन यह अनुरोध उनके नवीनतम फीडबैक साइट परिवर्तन में पोर्ट नहीं किया गया था। यदि माइक्रोसॉफ्ट ऐसे कार्यों को टी-एसक्यूएल में जोड़ता है, तो एनयूएलएल को अनदेखा करना है या नहीं, यह नियंत्रित करने का विकल्प होना बहुत अच्छा होगा।

इस बीच, उपरोक्त लोगों की तुलना में एक और अधिक सुरुचिपूर्ण तकनीक है जो एनयूएलएल को अनदेखा करते हुए वास्तविक कुल फ़ंक्शन सेमेन्टिक्स का उपयोग करके कॉलम में किसी भी प्रकार के कुल को रैखिक के रूप में गणना करती है। आप CROSS APPLY ऑपरेटर के संयोजन और एक टेबल-वैल्यू कंस्ट्रक्टर के विरुद्ध एक व्युत्पन्न तालिका क्वेरी का उपयोग करते हैं जो कॉलम को पंक्तियों में घुमाता है और एग्रीगेट को वास्तविक एग्रीगेट फ़ंक्शन के रूप में लागू करता है। यहां MIN और MAX गणनाओं को प्रदर्शित करने वाला एक उदाहरण दिया गया है, लेकिन आप इस तकनीक का उपयोग अपनी पसंद के किसी भी समग्र फ़ंक्शन के साथ कर सकते हैं:

SELECT col1, col2, col3, maxall, minall
FROM #T1 CROSS APPLY
  (SELECT MAX(mycol), MIN(mycol)
   FROM (VALUES(col1),(col2),(col3)) AS D1(mycol)) AS D2(maxall, minall);

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

col1        col2        col3        maxall      minall
----------- ----------- ----------- ----------- -----------
10          5           NULL        10          5

क्या होगा यदि आप विपरीत चाहते हैं? क्या होगा यदि आपको पंक्तियों में कुल की गणना करने की आवश्यकता है, लेकिन यदि कोई नल इनपुट है तो एक न्यूल उत्पन्न करें? उदाहरण के लिए, मान लीजिए कि आपको #T1 से सभी col1 मानों को जोड़ना है, लेकिन यदि कोई इनपुट न्यूल है तो NULL लौटाएं। इसे निम्नलिखित तकनीक से हासिल किया जा सकता है:

SELECT SUM(col1) * NULLIF(MIN(CASE WHEN col1 IS NULL THEN 0 ELSE 1 END), 0) AS sumall
FROM #T2;

आप एक केस एक्सप्रेशन के लिए एक मिन एग्रीगेट लागू करते हैं जो शून्य इनपुट के लिए शून्य और गैर-नल इनपुट के लिए देता है। यदि कोई NULL इनपुट है, तो MIN फ़ंक्शन का परिणाम 0 है, अन्यथा यह 1 है। फिर NULLIF फ़ंक्शन का उपयोग करके आप 0 परिणाम को NULL में परिवर्तित करते हैं। फिर आप NULLIF फ़ंक्शन के परिणाम को मूल योग से गुणा करें। यदि कोई NULL इनपुट है, तो आप मूल योग को NULL से एक NULL उपज से गुणा करते हैं। यदि कोई NULL इनपुट नहीं है, तो आप मूल योग के परिणाम को मूल योग से 1 से गुणा करते हैं।

किसी भी NULL इनपुट के लिए NULL उत्पन्न करने वाली रैखिक गणनाओं पर वापस, वही तर्क + ऑपरेटर का उपयोग करके स्ट्रिंग संयोजन पर लागू होता है, जैसा कि निम्न क्वेरी प्रदर्शित करती है:

USE TSQLV5;
 
SELECT empid, country, region, city,
  country + N',' + region + N',' + city AS emplocation
FROM HR.Employees;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

empid       country         region          city            emplocation
----------- --------------- --------------- --------------- ----------------
1           USA             WA              Seattle         USA,WA,Seattle
2           USA             WA              Tacoma          USA,WA,Tacoma
3           USA             WA              Kirkland        USA,WA,Kirkland
4           USA             WA              Redmond         USA,WA,Redmond
5           UK              NULL            London          NULL
6           UK              NULL            London          NULL
7           UK              NULL            London          NULL
8           USA             WA              Seattle         USA,WA,Seattle
9           UK              NULL            London          NULL

आप अल्पविराम का उपयोग विभाजक के रूप में करते हुए, कर्मचारियों के स्थान भागों को एक स्ट्रिंग में जोड़ना चाहते हैं। लेकिन आप NULL इनपुट को अनदेखा करना चाहते हैं। इसके बजाय, जब कोई भी इनपुट NULL होता है, तो आपको परिणाम के रूप में NULL मिलता है। कुछ लोग CONCAT_NULL_YIELDS_NULL सत्र विकल्प को बंद कर देते हैं, जिसके कारण एक NULL इनपुट को कॉन्सटेनेशन उद्देश्यों के लिए एक खाली स्ट्रिंग में परिवर्तित किया जाता है, लेकिन इस विकल्प की अनुशंसा नहीं की जाती है क्योंकि यह गैर-मानक व्यवहार को लागू करता है। इसके अलावा, NULL इनपुट होने पर आपको कई लगातार विभाजकों के साथ छोड़ दिया जाएगा, जो आमतौर पर वांछित व्यवहार नहीं है। एक अन्य विकल्प ISNULL या COALESCE फ़ंक्शंस का उपयोग करके NULL इनपुट को एक खाली स्ट्रिंग के साथ स्पष्ट रूप से बदलना है, लेकिन इसका परिणाम आमतौर पर लंबा वर्बोज़ कोड होता है। CONCAT_WS फ़ंक्शन का उपयोग करने के लिए एक और अधिक सुरुचिपूर्ण विकल्प है, जिसे SQL सर्वर 2017 में पेश किया गया था। यह फ़ंक्शन पहले इनपुट के रूप में प्रदान किए गए विभाजक का उपयोग करके, NULLs को अनदेखा करते हुए, इनपुट को जोड़ता है। यहाँ इस फ़ंक्शन का उपयोग करके समाधान क्वेरी है:

SELECT empid, country, region, city,
  CONCAT_WS(N',', country, region, city) AS emplocation
FROM HR.Employees;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

empid       country         region          city            emplocation
----------- --------------- --------------- --------------- ----------------
1           USA             WA              Seattle         USA,WA,Seattle
2           USA             WA              Tacoma          USA,WA,Tacoma
3           USA             WA              Kirkland        USA,WA,Kirkland
4           USA             WA              Redmond         USA,WA,Redmond
5           UK              NULL            London          UK,London
6           UK              NULL            London          UK,London
7           UK              NULL            London          UK,London
8           USA             WA              Seattle         USA,WA,Seattle
9           UK              NULL            London          UK,London

चालू/कहां/हो रहा है

फ़िल्टरिंग/मिलान उद्देश्यों के लिए WHERE, HAVING और ON क्वेरी क्लॉज़ का उपयोग करते समय, यह याद रखना महत्वपूर्ण है कि वे तीन-मूल्यवान विधेय तर्क का उपयोग करते हैं। जब आपके पास तीन-मूल्यवान-तर्क शामिल होते हैं, तो आप सटीक रूप से यह पहचानना चाहते हैं कि क्लॉज TRUE, FALSE और UNKNOWN मामलों को कैसे संभालता है। ये तीन खंड TRUE मामलों को स्वीकार करने और FALSE और UNKNOWN मामलों को अस्वीकार करने के लिए डिज़ाइन किए गए हैं।

इस व्यवहार को प्रदर्शित करने के लिए मैं संपर्क नामक एक तालिका का उपयोग करूंगा जिसे आप निम्न कोड चलाकर बनाते हैं और पॉप्युलेट करते हैं:.

DROP TABLE IF EXISTS dbo.Contacts;
GO
 
CREATE TABLE dbo.Contacts
(
  id INT NOT NULL 
    CONSTRAINT PK_Contacts PRIMARY KEY,
  name VARCHAR(10) NOT NULL,
  hourlyrate NUMERIC(12, 2) NULL
    CONSTRAINT CHK_Contacts_hourlyrate CHECK(hourlyrate > 0.00)
);
 
INSERT INTO dbo.Contacts(id, name, hourlyrate) VALUES
  (1, 'A', 100.00),(2, 'B', 200.00),(3, 'C', NULL);

ध्यान दें कि संपर्क 1 और 2 में प्रति घंटा की दर लागू है, और संपर्क 3 में नहीं है, इसलिए इसकी प्रति घंटा की दर NULL पर सेट है। सकारात्मक प्रति घंटा दर वाले संपर्कों की तलाश में निम्नलिखित प्रश्न पर विचार करें:

SELECT id, name, hourlyrate
FROM dbo.Contacts
WHERE hourlyrate > 0.00;

यह विधेय संपर्क 1 और 2 के लिए TRUE और संपर्क 3 के लिए UNKNOWN का मूल्यांकन करता है, इसलिए आउटपुट में केवल संपर्क 1 और 2 शामिल हैं:

id          name       hourlyrate
----------- ---------- -----------
1           A          100.00
2           B          200.00

यहां सोच यह है कि जब आप निश्चित हैं कि विधेय सत्य है, तो आप पंक्ति को वापस करना चाहते हैं, अन्यथा आप इसे छोड़ना चाहते हैं। यह पहली बार में तुच्छ लग सकता है, जब तक आप यह महसूस नहीं करते हैं कि कुछ भाषा तत्व जो विधेय का उपयोग करते हैं, वे अलग तरह से काम करते हैं।

जांच बाधा बनाम जांच विकल्प

एक CHECK बाधा एक उपकरण है जिसका उपयोग आप किसी विधेय के आधार पर तालिका में अखंडता को लागू करने के लिए करते हैं। जब आप तालिका में पंक्तियों को सम्मिलित या अद्यतन करने का प्रयास करते हैं तो विधेय का मूल्यांकन किया जाता है। क्वेरी फ़िल्टरिंग और मिलान क्लॉज़ के विपरीत, जो TRUE मामलों को स्वीकार करते हैं और FALSE और UNKNOWN मामलों को अस्वीकार करते हैं, एक CHECK बाधा को TRUE और UNKNOWN मामलों को स्वीकार करने और FALSE मामलों को अस्वीकार करने के लिए डिज़ाइन किया गया है। यहां सोच यह है कि जब आप निश्चित हैं कि विधेय गलत है, तो आप प्रयास किए गए परिवर्तन को अस्वीकार करना चाहते हैं, अन्यथा आप इसे अनुमति देना चाहते हैं।

यदि आप हमारी संपर्क तालिका की परिभाषा की जांच करते हैं, तो आप देखेंगे कि इसमें निम्न CHECK बाधा है, गैर-सकारात्मक प्रति घंटा दरों वाले संपर्कों को अस्वीकार करना:

CONSTRAINT CHK_Contacts_hourlyrate CHECK(hourlyrate > 0.00)

ध्यान दें कि बाधा उसी विधेय का उपयोग करती है जिसका उपयोग आपने पिछले क्वेरी फ़िल्टर में किया था।

सकारात्मक घंटे की दर से संपर्क जोड़ने का प्रयास करें:

INSERT INTO dbo.Contacts(id, name, hourlyrate) VALUES (4, 'D', 150.00);

यह प्रयास सफल होता है।

नल प्रति घंटा की दर से संपर्क जोड़ने का प्रयास करें:

INSERT INTO dbo.Contacts(id, name, hourlyrate) VALUES (5, 'E', NULL);

यह प्रयास भी सफल होता है, क्योंकि CHECK बाधा को TRUE और UNKNOWN मामलों को स्वीकार करने के लिए डिज़ाइन किया गया है। यही वह स्थिति है जहां एक क्वेरी फ़िल्टर और एक CHECK बाधा को अलग-अलग काम करने के लिए डिज़ाइन किया गया है।

एक गैर-सकारात्मक प्रति घंटा दर के साथ संपर्क जोड़ने का प्रयास करें:

INSERT INTO dbo.Contacts(id, name, hourlyrate) VALUES (6, 'F', -100.00);

यह प्रयास निम्न त्रुटि के साथ विफल होता है:

संदेश 547, स्तर 16, राज्य 0, पंक्ति 454
INSERT कथन CHECK बाधा "CHK_Contacts_hourlyrate" के विपरीत है। डेटाबेस "TSQLV5", तालिका "dbo.Contacts", कॉलम 'घंटा दर' में विरोध हुआ।

टी-एसक्यूएल आपको चेक विकल्प का उपयोग करके विचारों के माध्यम से संशोधनों की अखंडता को लागू करने की अनुमति देता है। जब तक आप दृश्य के माध्यम से संशोधन लागू करते हैं, तब तक कुछ लोग इस विकल्प को CHECK बाधा के समान उद्देश्य की सेवा के रूप में समझते हैं। उदाहरण के लिए, निम्नलिखित दृश्य पर विचार करें, जो विधेय प्रति घंटा> 0.00 के आधार पर फ़िल्टर का उपयोग करता है और इसे CHECK विकल्प के साथ परिभाषित किया गया है:

CREATE OR ALTER VIEW dbo.MyContacts
AS
SELECT id, name, hourlyrate
FROM dbo.Contacts
WHERE hourlyrate > 0.00
WITH CHECK OPTION;

जैसा कि यह पता चला है, एक CHECK बाधा के विपरीत, CHECK देखें विकल्प को TRUE मामलों को स्वीकार करने और FALSE और UNKNOWN दोनों मामलों को अस्वीकार करने के लिए डिज़ाइन किया गया है। तो यह वास्तव में अधिक व्यवहार करने के लिए डिज़ाइन किया गया है जैसे क्वेरी फ़िल्टर सामान्य रूप से अखंडता को लागू करने के उद्देश्य से भी करता है।

दृश्य के माध्यम से सकारात्मक प्रति घंटा दर के साथ एक पंक्ति सम्मिलित करने का प्रयास करें:

INSERT INTO dbo.MyContacts(id, name, hourlyrate) VALUES (7, 'G', 300.00);
में INSERT INTO करें

यह प्रयास सफल होता है।

दृश्य के माध्यम से NULL प्रति घंटा की दर से एक पंक्ति सम्मिलित करने का प्रयास करें:

INSERT INTO dbo.MyContacts(id, name, hourlyrate) VALUES (8, 'H', NULL);
में INSERT INTO करें

यह प्रयास निम्न त्रुटि के साथ विफल होता है:

Msg 550, Level 16, State 1, Line 473
इन्सर्ट या अपडेट का प्रयास विफल रहा क्योंकि लक्ष्य दृश्य या तो CHECK OPTION के साथ निर्दिष्ट करता है या एक ऐसे दृश्य को फैलाता है जो CHECK OPTION के साथ निर्दिष्ट करता है और ऑपरेशन के परिणामस्वरूप एक या अधिक पंक्तियाँ नहीं होती हैं CHECK Option बाधा के तहत अर्हता प्राप्त करें।

यहां सोच यह है कि एक बार जब आप दृश्य में चेक विकल्प जोड़ते हैं, तो आप केवल उन संशोधनों को अनुमति देना चाहते हैं जिनके परिणामस्वरूप दृश्य द्वारा लौटाई गई पंक्तियां होती हैं। यह एक CHECK बाधा के साथ सोच से थोड़ा अलग है - उन परिवर्तनों को अस्वीकार करें जिनके लिए आप निश्चित हैं कि विधेय गलत है। यह थोड़ा भ्रमित करने वाला हो सकता है। यदि आप चाहते हैं कि दृश्य उन संशोधनों की अनुमति दें जो प्रति घंटा की दर को NULL पर सेट करते हैं, तो आपको क्वेरी फ़िल्टर की आवश्यकता है ताकि उन्हें भी जोड़ा जा सके या प्रति घंटा IS NULL जोड़ा जा सके। आपको बस यह महसूस करने की आवश्यकता है कि एक CHECK बाधा और एक CHECK विकल्प को UNKNOWN मामले के संबंध में अलग तरह से काम करने के लिए डिज़ाइन किया गया है। पहला इसे स्वीकार करता है जबकि दूसरा इसे अस्वीकार करता है।

उपरोक्त सभी परिवर्तनों के बाद संपर्क तालिका को क्वेरी करें:

SELECT id, name, hourlyrate
FROM dbo.Contacts;

इस बिंदु पर आपको निम्न आउटपुट मिलना चाहिए:

id          name       hourlyrate
----------- ---------- -----------
1           A          100.00
2           B          200.00
3           C          NULL
4           D          150.00
5           E          NULL
7           G          300.00

अगर/जबकि/मामला

IF, WHILE और CASE भाषा तत्व विधेय के साथ काम करते हैं।

IF स्टेटमेंट इस प्रकार बनाया गया है:

IF <predicate>
  <statement or BEGIN-END block when TRUE>
ELSE
  <statement or BEGIN-END block when FALSE or UNKNOWN>

IF क्लॉज के बाद TRUE ब्लॉक और ELSE क्लॉज के बाद FALSE ब्लॉक होने की उम्मीद करना सहज है, लेकिन आपको यह समझने की जरूरत है कि ELSE क्लॉज वास्तव में सक्रिय हो जाता है जब विधेय FALSE या UNKNOWN होता है। सैद्धांतिक रूप से, तीन-मूल्यवान-तर्क भाषा में तीन मामलों को अलग करने के साथ एक IF कथन हो सकता था। कुछ इस तरह:

IF <predicate>
  WHEN TRUE
    <statement or BEGIN-END block when TRUE>
  WHEN FALSE
    <statement or BEGIN-END block when FALSE>
  WHEN UNKNOWN
    <statement or BEGIN-END block when UNKNOWN>

और तार्किक परिणामों के संयोजन को भी अनुमति दें ताकि यदि आप FALSE और UNKNOWN को एक अनुभाग में जोड़ना चाहते हैं, तो आप कुछ इस तरह का उपयोग कर सकते हैं:

IF <predicate>
  WHEN TRUE
    <statement or BEGIN-END block when TRUE>
  WHEN FALSE OR UNKNOWN
    <statement or BEGIN-END block when FALSE OR UNKNOWN>

इस बीच, आप IF-ELSE कथनों को नेस्ट करके और IS NULL ऑपरेटर के साथ ऑपरेंड में स्पष्ट रूप से NULLs की तलाश करके ऐसे निर्माणों का अनुकरण कर सकते हैं।

WHILE स्टेटमेंट में केवल TRUE ब्लॉक होता है। इसे इस प्रकार डिज़ाइन किया गया है:

WHILE <predicate>
  <statement or BEGIN-END block when TRUE>

लूप के शरीर को बनाने वाला स्टेटमेंट या BEGIN-END ब्लॉक सक्रिय होता है जबकि विधेय TURE होता है। जैसे ही विधेय FALSE या UNKNOWN होता है, WHILE लूप के बाद स्टेटमेंट पर कंट्रोल पास हो जाता है।

IF और WHILE के विपरीत, जो कोड निष्पादित करने वाले कथन हैं, CASE एक मान लौटाने वाला व्यंजक है। खोज . का सिंटैक्स CASE व्यंजक इस प्रकार है:

CASE
  WHEN <predicate 1> THEN <expression 1 when TRUE>
  WHEN <predicate 2> THEN <expression 2 when TRUE >
  ...
  WHEN <predicate n> THEN <expression n when TRUE >
  ELSE <else expression when all are FALSE or UNKNOWN>
END

CASE एक्सप्रेशन को THEN क्लॉज़ के बाद एक्सप्रेशन को वापस करने के लिए डिज़ाइन किया गया है जो TRUE का मूल्यांकन करने वाले पहले WHEN विधेय से मेल खाता है। यदि कोई ELSE खंड है, तो यह सक्रिय है यदि कोई WHEN विधेय TRUE नहीं है (सभी FALSE या UNKNOWN हैं)। एक स्पष्ट ELSE खंड के अभाव में, एक निहित ELSE NULL का उपयोग किया जाता है। यदि आप किसी अज्ञात मामले को अलग से संभालना चाहते हैं, तो आप IS NULL ऑपरेटर का उपयोग करके विधेय के संचालन में स्पष्ट रूप से NULLs की तलाश कर सकते हैं।

एक सरल CASE व्यंजक स्रोत व्यंजक और तुलनात्मक व्यंजकों के बीच अन्तर्निहित समानता-आधारित तुलनाओं का उपयोग करता है:

CASE <source expression>
  WHEN <comp expression 1> THEN <result expression 1 when TRUE>
  WHEN <comp expression 2> THEN <result expression 2 when TRUE >
  ...
  WHEN <comp expression n> THEN <result expression n when TRUE >
  ELSE <else result expression when all are FALSE or UNKNOWN>
END
हों

सरल CASE अभिव्यक्ति को तीन-मूल्यवान तर्क के संचालन के संदर्भ में खोजे गए CASE अभिव्यक्ति के समान डिज़ाइन किया गया है, लेकिन चूंकि तुलना एक अंतर्निहित समानता-आधारित तुलना का उपयोग करती है, आप अलग से UNKNOWN मामले को संभाल नहीं सकते हैं। WHEN क्लॉज में तुलनात्मक अभिव्यक्तियों में से किसी एक में NULL का उपयोग करने का प्रयास व्यर्थ है क्योंकि तुलना का परिणाम TRUE नहीं होगा, भले ही स्रोत अभिव्यक्ति NULL हो। निम्नलिखित उदाहरण पर विचार करें:

DECLARE @input AS INT = NULL;
 
SELECT CASE @input WHEN NULL THEN 'Input is NULL' ELSE 'Input is not NULL' END;

यह परोक्ष रूप से निम्नलिखित में परिवर्तित हो जाता है:

DECLARE @input AS INT = NULL;
 
SELECT CASE WHEN @input = NULL THEN 'Input is NULL' ELSE 'Input is not NULL' END;

नतीजतन, परिणाम है:

इनपुट न्यूल नहीं है

NULL इनपुट का पता लगाने के लिए, आपको खोजे गए CASE एक्सप्रेशन सिंटैक्स और IS NULL ऑपरेटर का उपयोग करना होगा, जैसे:

DECLARE @input AS INT = NULL;
 
SELECT CASE WHEN @input IS NULL THEN 'Input is NULL' ELSE 'Input is not NULL' END;

इस बार परिणाम है:

इनपुट शून्य है

मर्ज करें

MERGE स्टेटमेंट का उपयोग किसी स्रोत से डेटा को लक्ष्य में मर्ज करने के लिए किया जाता है। आप निम्न मामलों की पहचान करने और लक्ष्य के विरुद्ध कार्रवाई लागू करने के लिए मर्ज विधेय का उपयोग करते हैं:

  • एक स्रोत पंक्ति का लक्ष्य पंक्ति से मिलान किया जाता है (स्रोत पंक्ति के लिए एक मिलान मिलने पर सक्रिय होता है जहां मर्ज विधेय सत्य होता है):लक्ष्य के विरुद्ध अद्यतन या हटाएं लागू करें
  • एक स्रोत पंक्ति का लक्ष्य पंक्ति से मिलान नहीं होता (सक्रिय तब होता है जब स्रोत पंक्ति के लिए कोई मिलान नहीं मिलता है जहां मर्ज विधेय TRUE है, बल्कि सभी विधेय FALSE या UNKNOWN है):लक्ष्य के विरुद्ध INSERT लागू करें
  • एक लक्ष्य पंक्ति का स्रोत पंक्ति से मिलान नहीं होता (सक्रिय तब होता है जब लक्ष्य पंक्ति के लिए कोई मिलान नहीं मिलता है जहां मर्ज विधेय TRUE है, बल्कि सभी विधेय FALSE या UNKNOWN है):लक्ष्य के विरुद्ध UPDATE या DELETE लागू करें। ली>

सभी तीन परिदृश्य TRUE को एक समूह और FALSE या UNKNOWN को दूसरे से अलग करते हैं। आपको TRUE को संभालने, FALSE को संभालने और UNKNOWN मामलों को संभालने के लिए अलग-अलग सेक्शन नहीं मिलते हैं।

इसे प्रदर्शित करने के लिए, मैं T3 नामक एक तालिका का उपयोग करूँगा जिसे आप निम्न कोड चलाकर बनाते हैं और पॉप्युलेट करते हैं:

DROP TABLE IF EXISTS dbo.T3;
GO
 
CREATE TABLE dbo.T3(col1 INT NULL, col2 INT NULL, CONSTRAINT UNQ_T3 UNIQUE(col1));
 
INSERT INTO dbo.T3(col1) VALUES(1),(2),(NULL);

निम्नलिखित MERGE कथन पर विचार करें:

MERGE INTO dbo.T3 AS TGT
USING (VALUES(1, 100), (3, 300)) AS SRC(col1, col2)
  ON SRC.col1 = TGT.col1
WHEN MATCHED THEN UPDATE
  SET TGT.col2 = SRC.col2
WHEN NOT MATCHED THEN INSERT(col1, col2) VALUES(SRC.col1, SRC.col2)
WHEN NOT MATCHED BY SOURCE THEN UPDATE
  SET col2 = -1;
 
SELECT col1, col2 FROM dbo.T3;

स्रोत पंक्ति जहां col1 1 है, लक्ष्य पंक्ति से मेल खाती है जहां col1 1 है (विधेय सत्य है) और इसलिए लक्ष्य पंक्ति का col2 100 पर सेट है।

स्रोत पंक्ति जहां col1 3 है, किसी भी लक्ष्य पंक्ति से मेल नहीं खाती है (सभी विधेय के लिए FALSE या UNKNOWN है) और इसलिए T3 में एक नई पंक्ति डाली जाती है जिसमें 3 col1 मान के रूप में और 300 col2 मान के रूप में होता है।

लक्ष्य पंक्तियाँ जहाँ col1 2 है और जहाँ col1 NULL है, किसी भी स्रोत पंक्ति से मेल नहीं खाती (सभी पंक्तियों के लिए विधेय FALSE या UNKNOWN है) और इसलिए दोनों ही मामलों में लक्ष्य पंक्तियों में col2 -1 पर सेट है।

उपरोक्त MERGE स्टेटमेंट को निष्पादित करने के बाद T3 के विरुद्ध क्वेरी निम्न आउटपुट देता है:

col1        col2
----------- -----------
1           100
2           -1
NULL        -1
3           300

टेबल T3 को चारों ओर रखें; इसका उपयोग बाद में किया जाता है।

विशिष्टता और समूहीकरण

समानता और असमानता संचालकों का उपयोग करके की जाने वाली तुलनाओं के विपरीत, विशिष्टता और समूह उद्देश्यों के लिए की गई तुलना NULLs को एक साथ समूहित करती है। एक NULL को दूसरे NULL से अलग नहीं माना जाता है, लेकिन एक NULL को गैर-NULL मान से अलग माना जाता है। नतीजतन, DISTINCT क्लॉज को लागू करने से NULLs की डुप्लिकेट घटनाएँ दूर हो जाती हैं। निम्न क्वेरी इसे प्रदर्शित करती है:

SELECT DISTINCT country, region FROM HR.Employees;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

country         region
--------------- ---------------
UK              NULL
USA             WA

संयुक्त राज्य अमेरिका और क्षेत्र NULL के साथ कई कर्मचारी हैं, और डुप्लिकेट को हटाने के बाद परिणाम संयोजन की केवल एक घटना दिखाता है।

विशिष्टता की तरह, समूह भी NULLs को एक साथ समूहित करता है, जैसा कि निम्न क्वेरी दर्शाती है:

SELECT country, region, COUNT(*) AS numemps
FROM HR.Employees
GROUP BY country, region;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

country         region          numemps
--------------- --------------- -----------
UK              NULL            4
USA             WA              5

फिर से, देश यूके और न्यूल क्षेत्र के सभी चार कर्मचारियों को एक साथ समूहीकृत किया गया।

आदेश देना

ऑर्डरिंग एकाधिक एनयूएलएल को समान ऑर्डरिंग मान के रूप में मानता है। SQL मानक इसे कार्यान्वयन के लिए छोड़ देता है यह चुनने के लिए कि क्या NULLs को पहले या अंतिम गैर-NULL मानों की तुलना में ऑर्डर करना है। माइक्रोसॉफ्ट ने एनयूएलएल को एसक्यूएल सर्वर में गैर-एनयूएलएल की तुलना में कम ऑर्डरिंग मान रखने के लिए चुना है, इसलिए आरोही क्रम दिशा का उपयोग करते समय, टी-एसक्यूएल पहले एनयूएलएल का आदेश देता है। निम्न क्वेरी इसे प्रदर्शित करती है:

SELECT id, name, hourlyrate
FROM dbo.Contacts
ORDER BY hourlyrate;

यह क्वेरी निम्न आउटपुट उत्पन्न करती है:

id          name       hourlyrate
----------- ---------- -----------
3           C          NULL
5           E          NULL
1           A          100.00
4           D          150.00
2           B          200.00
7           G          300.00

अगले महीने मैं इस विषय पर और बातें जोड़ूंगा, उन मानक तत्वों पर चर्चा करूंगा जो आपको NULL ऑर्डरिंग व्यवहार और T-SQL में उन तत्वों के लिए वर्कअराउंड पर नियंत्रण प्रदान करते हैं।

विशिष्टता

एक अद्वितीय बाधा या एक अद्वितीय अनुक्रमणिका का उपयोग करके एक नलबल कॉलम पर विशिष्टता लागू करते समय, टी-एसक्यूएल एनयूएलएल को गैर-नल मानों की तरह मानता है। यह डुप्लीकेट NULLs को अस्वीकार करता है जैसे कि एक NULL दूसरे NULL से अद्वितीय नहीं है।

याद रखें कि हमारी तालिका T3 में col1 पर परिभाषित एक अद्वितीय बाधा है। यहां इसकी परिभाषा दी गई है:

CONSTRAINT UNQ_T3 UNIQUE(col1)

इसकी वर्तमान सामग्री देखने के लिए क्वेरी T3:

SELECT * FROM dbo.T3;

यदि आपने इस आलेख में पिछले उदाहरणों से T3 के विरुद्ध सभी संशोधनों को चलाया है, तो आपको निम्न आउटपुट प्राप्त करना चाहिए:

col1        col2
----------- -----------
1           100
2           -1
NULL        -1
3           300

col1 में NULL के साथ दूसरी पंक्ति जोड़ने का प्रयास करें:

INSERT INTO dbo.T3(col1, col2) VALUES(NULL, 400);

आपको निम्न त्रुटि मिलती है:

Msg 2627, Level 14, State 1, Line 558
UNIQUE KEY बाधा 'UNQ_T3' का उल्लंघन। ऑब्जेक्ट 'dbo.T3' में डुप्लिकेट कुंजी सम्मिलित नहीं कर सकता। डुप्लिकेट कुंजी मान () है।

यह व्यवहार वास्तव में गैर-मानक है। अगले महीने मैं मानक विनिर्देश और टी-एसक्यूएल में इसका अनुकरण करने का तरीका बताऊंगा।

निष्कर्ष

श्रृंखला के इस दूसरे भाग में NULL जटिलताओं पर मैंने विभिन्न T-SQL तत्वों के बीच NULL उपचार विसंगतियों पर ध्यान केंद्रित किया। मैंने रैखिक बनाम कुल संगणना, फ़िल्टरिंग और मिलान खंड, CHECK बाधा बनाम CHECK विकल्प, IF, WHILE और CASE तत्वों, MERGE कथन, विशिष्टता और समूहीकरण, क्रम और विशिष्टता को कवर किया। जिन विसंगतियों को मैंने कवर किया है, वे इस बात पर और जोर देते हैं कि आपके द्वारा उपयोग किए जा रहे प्लेटफॉर्म में एनयूएलएल के उपचार को सही ढंग से समझना कितना महत्वपूर्ण है, यह सुनिश्चित करने के लिए कि आप सही और मजबूत कोड लिखते हैं। अगले महीने मैं SQL मानक NULL उपचार विकल्पों को कवर करके श्रृंखला जारी रखूंगा जो T-SQL में उपलब्ध नहीं हैं, और T-SQL में समर्थित वर्कअराउंड प्रदान करते हैं।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ubuntu 20.04 पर अरंगोडीबी कैसे स्थापित करें?

  2. क्या नए इंडेक्स कॉलम कुंजी में होने चाहिए, या शामिल हैं?

  3. अल्फा एनीवेयर में Salesforce.com के साथ कार्य करना

  4. SQL में Alter Table Statement का प्रयोग कैसे करें?

  5. क्या RID लुकअप की लुकअप की तुलना में तेज़ है?