UNKNOWN을 반환 할 수있는 부울 식 반전 얻는다는 것을 안다

나는 테이블이있다

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

TRUE, FALSE 또는 (SQL의 삼항 논리로 인해) UNKNOWN으로 평가할 수있는 T-SQL 부울 식

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수없는 “돌이킬 수없는”표현을 얻는다는 것을 안다 :

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...

이 expession을 “반전”하는 일반적인 방법이 있습니까? 하위 표현식에서 작동하는 경우 보너스 포인트 :

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

SQL Server 2008-2014를 지원해야하지만 2008보다 최신 버전이 필요한 우아한 솔루션이 있다면 그것에 대해서도 관심이 있습니다.



답변

이진 결과 (예 : 1 또는 0)를 반환하는 CASE 식으로 조건을 묶을 수 있습니다.

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

표현식을 무효화하면 someColumn 이 null 인 행을 포함하여 동일한 데이터 소스의 다른 모든 행이 제공됩니다 .

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 Server 2012부터 IIF 함수 도 있습니다. 이는 위와 같이 이진 CASE를 감싸는 래퍼입니다. 따라서이 CASE 표현식은 다음과 같습니다.

CASE WHEN someColumn = someValue THEN 1 ELSE 0 END

IIF를 사용하여 다시 작성하면 다음과 같습니다.

IIF(someColumn = someValue, 1, 0)

그리고 CASE 표현식과 정확히 같은 방식으로 사용할 수 있습니다. 성능에는 차이가 없으며 코드 만 약간 더 간결하고 아마도 더 깔끔합니다.


답변

나에게 일어난 첫 번째 생각 :

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);

참조가 EXISTS (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)
    ;


답변

하위 표현식을 미리 작성하지 않으려면 다음을 사용할 수 있습니다 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합니다. 주된 단점은 두 번째 값이 같지 않다는 것입니다. 그러나 실제 값을 미리 모르는 경우에만 문제가됩니다. 이 경우 Hanno Binder가 제안하고 사용 하는 것처럼 COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'( 'someValue'실제로 매개 변수화 되는 위치) 수행 할 수 있습니다.

COALESCE SQL Server 2005부터 사용 가능하도록 문서화되었습니다.

이와 같이 쿼리를 망치면 (여기서 권장되는 방법 중 하나를 사용하여) 데이터베이스에서 쿼리를 최적화하기가 더 어려워 질 수 있습니다. 대규모 데이터 세트의 경우 IS NULL버전을 최적화하기가 더 쉽습니다.


답변

내장 EXCEPT 세트 연산자가있어 첫 번째 쿼리에서 두 번째 쿼리의 결과를 효과적으로 제거합니다.

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


답변

COALESCE를 사용할 수 있습니까?

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


답변