UNKNOWN을 반환 할 수있는 부울 식 반전


11

나는 테이블이있다

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보다 최신 버전이 필요한 우아한 솔루션이 있다면 그것에 대해서도 관심이 있습니다.

답변:


15

이진 결과 (예 : 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 표현식과 정확히 같은 방식으로 사용할 수 있습니다. 성능에는 차이가 없으며 코드 만 약간 더 간결하고 아마도 더 깔끔합니다.


좋은 생각이야! CASE를 사용하여 부울 표현식을 작업 할 수있는 표현식으로 "변환"한 다음 비교를 사용하여 부울 표현식으로 다시 "변환"하십시오.
Heinzi

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

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

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

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

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


1
COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'"someValue"및 테이블의 모든 데이터에 대해 작동해야합니다.
JimmyB

2

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

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

그것이 작은 테이블이기를 바랍니다 :-)
Lennart

-4

COALESCE를 사용할 수 있습니까?

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

4
예, COALESCE는 사용 가능하지만 아니요, 작동하지 않습니다. (a) COALESCE는 부울 식을 허용하지 않으며 (그렇지만 ISNULL도 마찬가지 임) (b) 진실 값 FALSE는 SQL에서 직접 사용할 수 없습니다. 리터럴. 시도하면 구문 오류가 발생합니다.
Heinzi

@ Heinzi-시도해 보았습니다. 그것이 효과가 있었기 때문에 게시했습니다. 아마도 T-SQL에서는 작동하지 않지만 Postgres 및 MySQL에서는 괜찮습니다.
Malvolio

2
@Malvolio : 문제가 되는 태그 sql-server하지만,하지, mysqlpostgresql.
Andriy M

@Malvolio Postgres에는 BOOLEAN유형이 있고 MySQL에는 함수의 BOOLEAN매개 변수가 될 수 있는 (가짜) 유형이 있기 때문 COALESCE()입니다. 질문에 sql-agnostic또는 태그가 붙어 있으면 sql-standard대답은 괜찮을 것입니다.
ypercubeᵀᴹ

@ ypercubeᵀᴹ-어, 내가 무엇을 말할 수 있습니까? 더 나은 데이터베이스를 얻으십시오.
Malvolio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.