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


11

उदाहरण

मेरे पास एक टेबल है

ID  myField
------------
 1  someValue
 2  NULL
 3  someOtherValue

और एक टी-एसक्यूएल बूलियन अभिव्यक्ति जो TRUE, FALSE या (SQL के टर्नरी लॉजिक के कारण) का मूल्यांकन कर सकती है: UNKNOWN:

SELECT * FROM myTable WHERE myField = 'someValue'

-- yields record 1

यदि मैं अन्य सभी रिकॉर्ड प्राप्त करना चाहता हूं , तो मैं केवल अभिव्यक्ति को नकार नहीं सकता

SELECT * FROM myTable WHERE NOT (myField = 'someValue')

-- yields only record 3

मुझे पता है कि ऐसा क्यों होता है (टर्नरी लॉजिक), और मुझे पता है कि इस विशिष्ट मुद्दे को कैसे हल किया जाए।

मुझे पता है कि मैं सिर्फ उपयोग कर सकता हूं myField = 'someValue' AND NOT myField IS NULLऔर मुझे एक "उल्टा" अभिव्यक्ति मिलती है, जो कभी भी UNKNOWN नहीं करता है:

SELECT * FROM myTable WHERE NOT (myField = 'someValue' AND myField IS NOT NULL)

-- yields records 2 and 3, hooray!

सामान्य मामला

अब, सामान्य मामले के बारे में बात करते हैं। मान लीजिए कि myField = 'someValue'मेरे बजाय कुछ जटिल अभिव्यक्ति है जिसमें बहुत सारे क्षेत्र और स्थितियां शामिल हैं, शायद उपश्रेणियाँ:

SELECT * FROM myTable WHERE ...some complex Boolean expression...

क्या इस निष्कासन का "उल्टा" करने का एक सामान्य तरीका है? बोनस अंक अगर यह सबसेक्स के लिए काम करता है:

SELECT * FROM myTable 
 WHERE ...some expression which stays... 
   AND ...some expression which I might want to invert...

मुझे SQL Server 2008-2014 का समर्थन करने की आवश्यकता है, लेकिन अगर 2008 की तुलना में एक नए संस्करण की आवश्यकता के लिए एक सुंदर समाधान है, तो मुझे इसके बारे में भी सुनने में दिलचस्पी है।

जवाबों:


15

उदाहरण के लिए 1 या 0 के लिए बाइनरी रिजल्ट लौटाने वाली CASE अभिव्यक्ति में आप इस स्थिति को शामिल कर सकते हैं:

SELECT
  ...
FROM
  ...
WHERE
  CASE WHEN someColumn = someValue THEN 1 ELSE 0 END = 1
;

अभिव्यक्ति की उपेक्षा करने से आपको एक ही डेटा स्रोत से अन्य सभी पंक्तियाँ मिलेंगी , जिनमें वे भी शामिल हैं जहाँ कुछ कॉलम शून्य है:

SELECT
  ...
FROM
  ...
WHERE
  NOT CASE WHEN someColumn = someValue THEN 1 ELSE 0 END = 1
  -- or: CASE WHEN someColumn = someValue THEN 1 ELSE 0 END <> 1
;

SQL सर्वर 2012 के बाद से आपके पास IIF फ़ंक्शन भी है , जो कि ऊपर की तरह एक बाइनरी केस के चारों ओर एक आवरण है। तो, यह मामला अभिव्यक्ति:

CASE WHEN someColumn = someValue THEN 1 ELSE 0 END

IIF का उपयोग करके फिर से लिखे जाने पर ऐसा दिखेगा:

IIF(someColumn = someValue, 1, 0)

और आप इसे उसी तरह से उपयोग कर सकते हैं जैसे कि CASE अभिव्यक्ति। प्रदर्शन में कोई अंतर नहीं होगा, केवल कोड थोड़ा अधिक संक्षिप्त होगा, संभवतः इस तरह से क्लीनर भी।


यह एक अच्छा विचार है! एक बूलियन अभिव्यक्ति को "कन्वर्ट" करने के लिए CASE का उपयोग करें जिसके साथ काम कर सकते हैं, और फिर एक बूलियन अभिव्यक्ति के लिए इसे "कन्वर्ट" करने के लिए तुलना का उपयोग करें।
हेंजी

10

पहला विचार जो मेरे साथ होता है:

DECLARE @T AS table (c1 integer NULL);

INSERT @T (c1)
VALUES (1), (NULL), (2);

-- Original expression c1 = 1
SELECT T.c1
FROM @T AS T
WHERE c1 = 1;

यह दिखाता है:

परिणाम

-- Negated
SELECT T.c1
FROM @T AS T
WHERE NOT EXISTS (SELECT 1 WHERE c1 = 1);

यह दिखाता है:

नकारात्मक परिणाम

यह रास्ते पर निर्भर करता है EXISTSहमेशा सही या गलत , कभी अज्ञात नहीं लौटता । के लिए की जरूरत SELECT 1 WHEREदुर्भाग्य से आवश्यक है, लेकिन यह आपकी आवश्यकता के लिए व्यावहारिक हो सकता है, उदाहरण के लिए:

sql = "
    SELECT * 
    FROM someTable 
    WHERE " + someExpression + 
    " AND NOT EXISTS (SELECT 1 WHERE " + 
    someOtherExpression + ")";
result = executeAndShow(sql);

देखें मौजूद है (Transact-SQL)


एक और अधिक जटिल काम उदाहरण से पता चलता है कि व्यक्तिगत विधेयकों को पलटने के लिए EXISTSया तो या CASE/IIFतरीकों को कैसे लागू किया जा सकता है:

DECLARE @T AS table 
(
    c1 integer NULL,
    c2 integer NULL,
    c3 integer NULL
);

INSERT @T 
    (c1, c2, c3)
VALUES 
    (1, NULL, 2),
    (2, 2, 3),
    (NULL, 1, 4);

कोड:

-- Original
SELECT 
    T.c1,
    T.c2,
    T.c3
FROM @T AS T
WHERE
    1 = 1
    -- Predicate #1
    AND T.c1 = 2
    -- Predicate #2
    AND T.c2 =
    (
        SELECT MAX(T2.c2)
        FROM @T AS T2
        WHERE T2.c2 IS NOT NULL
    )
    -- Predicate #3
    AND T.c3 IN (3, 4)
    ;

-- Invert predicates #1 and #2
SELECT 
    T.c1,
    T.c2,
    T.c3
FROM @T AS T
WHERE
    1 = 1
    AND NOT EXISTS (SELECT 1 WHERE 1 = 1
        -- Predicate #1
        AND T.c1 = 2)
    AND NOT EXISTS (SELECT 1 WHERE 1 = 1
        -- Predicate #2
            AND T.c2 =
            (
                SELECT MAX(T2.c2)
                FROM @T AS T2
                WHERE T2.c2 IS NOT NULL
            ))
    -- Predicate #3
    AND T.c3 IN (3, 4)
    ;

3

यदि आपको उप-अभिव्यक्तियों को फिर से लिखने में कोई आपत्ति नहीं है, तो आप इसका उपयोग कर सकते हैं COALESCE:

SELECT *
FROM myTable
WHERE NOT (COALESCE(myField, 'notSomeValue') = 'someValue')

आपको यह सुनिश्चित करना चाहिए कि 'notSomeValue'क्या अलग है 'someValue'; अधिमानतः, यह कॉलम के लिए कुछ पूरी तरह से अवैध मूल्य होगा। (यह निश्चित रूप से नहीं हो सकता है NULL।) यह नकारात्मक करना आसान है, भले ही आपके पास एक लंबी सूची हो:

SELECT *
FROM myTable
WHERE NOT (
    COALESCE(myField, 'notSomeValue') = 'someValue' AND
    COALESCE(myField2, 'notSomeValue') = 'someValue2' AND
    COALESCE(myField3, 'notSomeValue') = 'someValue3' AND
    COALESCE(myField4, 'notSomeValue') = 'someValue4'
)

क्लीनर, सरल, और तुलना में अधिक स्पष्ट CASEया IIF, मेरी राय में। मुख्य नकारात्मक पहलू यह है कि आपको पता है कि दूसरा मूल्य नहीं है, लेकिन यह वास्तव में केवल एक समस्या है यदि आप वास्तविक मूल्य को सामने नहीं जानते हैं। उस मामले में, आप कर सकते हैं जैसा कि हैनो बाइंडर सुझाव देता है और उपयोग करता है COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'(जहां 'someValue'वास्तव में पैरामीटर किया जाएगा)।

COALESCE SQL सर्वर 2005 से आगे उपलब्ध होने के लिए प्रलेखित है।

ध्यान रखें कि इस तरह से अपनी क्वेरी के साथ खिलवाड़ करना (यहाँ सुझाई गई किसी भी विधि का उपयोग करके) डेटाबेस के लिए आपकी क्वेरी को अनुकूलित करना अधिक कठिन बना सकता है। बड़े डेटासेट के लिए, IS NULLसंस्करण को अनुकूलित करना आसान है।


1
COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'तालिका में किसी भी "someValue" और किसी भी डेटा के लिए काम करना चाहिए।
12

2

वहाँ अंतर्निहित EXCEPT सेट ऑपरेटर है, जो प्रभावी रूप से, पहले क्वेरी से दूसरी क्वेरी के परिणाम निकालता है।

select * from table
except
select * from table
where <really complex predicates>

चलो आशा करते हैं कि यह एक छोटी सी मेज है :-)
लेनार्ट

-4

COALESCE उपलब्ध है?

SELECT * FROM myTable WHERE NOT COALESCE(myField = 'someValue', FALSE)

4
हां, COALESCE उपलब्ध है, लेकिन नहीं, यह काम नहीं करेगा: (a) COALESCE एक बुलियन एक्सप्रेशन को स्वीकार नहीं करेगा (न ही ISNULL, वैसे) और (b) सत्य मान FALSE SQL SQL में सीधे उपलब्ध नहीं है एक शाब्दिक। इसे आज़माएं, और आपको एक सिंटैक्स त्रुटि मिलेगी।
हेंजी

@ हेंज़ी - मैंने इसे आज़माया, यह काम किया, इसीलिए मैंने इसे पोस्ट किया। शायद यह टी-एसक्यूएल पर काम नहीं करता है, लेकिन यह पोस्टग्रैज और माईएसक्यूएल पर ठीक है।
मालवोलियो

2
@Malvolio: सवाल है टैग किया sql-serverहै, हालांकि, नहीं mysqlया postgresql
एंड्री एम

@Malvolio ऐसा इसलिए है क्योंकि Postgres में एक BOOLEANप्रकार है और MySQL में एक (फेक) BOOLEANप्रकार है जो COALESCE()फ़ंक्शन का पैरामीटर हो सकता है। यदि प्रश्न के साथ टैग किया गया था sql-agnosticया sql-standard, उत्तर ठीक होगा।
ypercube y

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