development

NOT IN 절 및 NULL 값

big-blog 2020. 4. 16. 08:21
반응형

NOT IN 절 및 NULL 값


이 문제는 하나의 not in where제약 조건을 사용하는 동일한 쿼리 와 다른 쿼리에 대해 다른 레코드 수를 얻었을 때 발생 했습니다 left join. not in제약 조건 의 테이블에 하나의 null 값 (잘못된 데이터)이있어 해당 쿼리가 0 레코드 수를 반환했습니다. 나는 왜 그런지 이해하지만 개념을 완전히 이해하는 데 도움을 줄 수 있습니다.

간단히 말해 쿼리 A는 결과를 반환하지만 B는 그렇지 않은 이유는 무엇입니까?

A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)

이것은 SQL Server 2005에있었습니다. 또한 호출 set ansi_nulls off하면 B가 결과를 반환 한다는 것을 알았습니다 .


쿼리 A는 다음과 같습니다.

select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null

3 = 3사실 이므로 결과를 얻습니다.

쿼리 B는 다음과 같습니다.

select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null

경우 ansi_nulls에이다, 3 <> nullUNKNOWN는 술어 평가됩니다 그래서 UNKNOWN에, 당신은 어떤 행을하지 않습니다.

ansi_nulls꺼져, 3 <> null술어 평가하여 이렇게 참으로, 사실, 당신은 행을 얻을.


NULL을 사용할 때마다 실제로는 3 값 논리를 처리합니다.

첫 번째 쿼리는 WHERE 절이 다음과 같이 평가 될 때 결과를 반환합니다.

    3 = 1 or 3 = 2 or 3 = 3 or 3 = null
which is:
    FALSE or FALSE or TRUE or UNKNOWN
which evaluates to 
    TRUE

두 번째 것 :

    3 <> 1 and 3 <> 2 and 3 <> null
which evaluates to:
    TRUE and TRUE and UNKNOWN
which evaluates to:
    UNKNOWN

UNKNOWN은 FALSE와 같지 않으므로 다음을 호출하여 쉽게 테스트 할 수 있습니다.

select 'true' where 3 <> null
select 'true' where not (3 <> null)

두 쿼리 모두 결과를 제공하지 않습니다

UNKNOWN이 FALSE와 같으면 첫 번째 쿼리가 FALSE를 제공한다고 가정하면 두 번째 쿼리는 NOT (FALSE)과 같았으므로 TRUE로 평가해야합니다.
그렇지 않습니다.

SqlServerCentral에 대한이 주제에 대한 좋은 기사가 있습니다.

NULL과 Three-Valued Logic의 전체 문제는 처음에는 약간 혼란 스러울 수 있지만 TSQL에서 올바른 쿼리를 작성하려면 이해해야합니다.

내가 추천하는 또 다른 기사는 SQL Aggregate Functions 및 NULL 입니다.


NOT IN 알 수없는 값과 비교할 때 0 개의 레코드를 반환

이후 NULL알 수없는이하는 NOT IN포함하는 쿼리 NULL또는 NULL가능한 값의 목록에들 항상 반환됩니다 0확인하는 것이 할 수있는 방법이 없기 때문에 기록을 NULL값이 테스트중인 값이 없습니다.


IS NULL을 사용하지 않으면 널과 비교는 정의되지 않습니다.

따라서 3을 NULL (쿼리 A)과 비교할 때 undefined가 반환됩니다.

즉 (1,2, null)에 3이면 SELECT 'true', (1,2, null)에 3이면 SELECT 'true'

NOT (UNDEFINED)이 아직 정의되지 않았지만 TRUE가 아닌 동일한 결과를 생성합니다.


글을 쓰는 시점에서이 질문의 제목은

SQL NOT IN 제한 조건 및 NULL 값

질문의 텍스트에서 문제는 SELECTSQL DDL 대신 SQL DML 쿼리 에서 발생한 것으로 보입니다 CONSTRAINT.

그러나 특히 제목의 문구를 감안할 때, 여기에 작성된 일부 진술은 잠재적으로 잘못된 진술, 즉 (낙원)

술어가 UNKNOWN으로 평가되면 행이 없습니다.

이는 SQL DML의 경우이지만 제약 조건을 고려할 때 효과가 다릅니다.

질문의 술어에서 직접 가져온 두 가지 제약 조건이있는이 매우 간단한 표를 고려하십시오 (@Brannon의 탁월한 답변으로 해결 됨).

DECLARE @T TABLE 
(
 true CHAR(4) DEFAULT 'true' NOT NULL, 
 CHECK ( 3 IN (1, 2, 3, NULL )), 
 CHECK ( 3 NOT IN (1, 2, NULL ))
);

INSERT INTO @T VALUES ('true');

SELECT COUNT(*) AS tally FROM @T;

@Brannon의 답변에 따라 첫 번째 제약 조건 (을 사용하여 IN)은 TRUE로 평가되고 두 ​​번째 제약 조건 (을 사용하여 NOT IN)은 UNKNOWN으로 평가됩니다. 그러나 삽입이 성공합니다! 따라서이 경우 실제로 행을 삽입 했으므로 "행이 없습니다"라고 말하는 것이 정확하지 않습니다.

위의 효과는 SQL-92 표준과 관련하여 실제로 올바른 효과입니다. SQL-92 스펙에서 다음 섹션을 비교하고 대조하십시오

7.6 where 절

의 결과는 검색 조건의 결과가 참인 T의 행에 대한 테이블입니다.

무결성 제약

지정된 검색 조건이 테이블의 행에 대해 거짓이 아닌 경우에만 테이블 점검 제한 조건이 충족됩니다.

다시 말해:

SQL DML 에서 조건이 "참" 이 아니기WHERE 때문에 UNKNOWN으로 평가 되면 행이 결과에서 제거됩니다 .

이 때문에 그들이 UNKNOWN으로 평가하는 경우 SQL DDL (예 : 제약)에서 행이 결과에서 제거되지 않는 조건을 만족 "거짓 아니다".

SQL DML과 SQL DDL의 효과는 각각 모순되는 것처럼 보일 수 있지만 UNKNOWN 결과에 제약 조건을 충족시킬 수있게하여 (의미를 더 정확하게 제공함으로써 제약 조건을 충족시키지 못하도록하는) 실질적인 이유가 있습니다. :이 동작이 없으면 모든 제약 조건이 null을 명시 적으로 처리해야하며 언어 설계 관점에서 볼 때 매우 불만족 스럽습니다 (코더에게는 올바른 고통은 말할 것도 없습니다!)

추신 : "알 수없는 제약 조건을 충족시키지 못합니다"와 같은 논리를 따르는 것이 어렵다고 생각되면 SQL DDL의 nullable 열과 SQL의 모든 것을 피 함으로써이 모든 것을 피할 수 있다고 생각하십시오. 널을 생성하는 DML (예 : 외부 조인)!


A에서 3은 세트의 각 멤버에 대해 동등성을 테스트하여 (FALSE, FALSE, TRUE, UNKNOWN)을 산출합니다. 요소 중 하나가 TRUE이므로 조건이 TRUE입니다. (일부 단락이 발생할 수도 있으므로 첫 번째 TRUE에 도달하자마자 실제로 중지되고 3 = NULL로 평가되지 않습니다.)

B에서는 조건이 NOT (3 in (1,2, null))으로 평가되고 있다고 생각합니다. 3 세트 수율 (FALSE, FALSE, UNKNOWN)에 대한 동등성을 테스트하여 UNKNOWN으로 집계합니다. NOT (알 수 없음)은 알 수없는 결과를 낳습니다. 따라서 전반적인 상태의 진실은 알 수 없으며 결국 본질적으로 FALSE로 취급됩니다.


NOT IN (subquery)null을 올바르게 처리하지 않으므로 여기에 대한 답변에서 결론을 내릴 수 있습니다 NOT EXISTS. 그러나 그러한 결론은 시기상조 일 수 있습니다. Chris Date (데이터베이스 프로그래밍 및 디자인, Vol 2 No 9, 1989 년 9 월)로 인정되는 다음 시나리오 NOT IN에서는 null을 올바르게 처리하고 대신 올바른 결과를 반환합니다 NOT EXISTS.

부품 ( )을 수량 ( ) 으로 공급하는 것으로 알려진 sp공급 업체 ( sno) 를 나타내는 고려하십시오 . 이 테이블에는 현재 다음 값이 있습니다.pnoqty

      VALUES ('S1', 'P1', NULL), 
             ('S2', 'P1', 200),
             ('S3', 'P1', 1000)

수량은 무효화됩니다. 즉, 공급 업체가 수량을 모르더라도 부품을 공급하는 것으로 알려진 사실을 기록 할 수 있습니다.

이 작업은 알려진 공급 부품 번호 'P1'이지만 1000 수량이 아닌 공급 업체를 찾는 것입니다.

다음은 NOT IN공급 업체 'S2'만 올바르게 식별하는 데 사용 됩니다.

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1', NULL ), 
                       ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT DISTINCT spx.sno
  FROM sp spx
 WHERE spx.pno = 'P1'
       AND 1000 NOT IN (
                        SELECT spy.qty
                          FROM sp spy
                         WHERE spy.sno = spx.sno
                               AND spy.pno = 'P1'
                       );

그러나 아래 쿼리는 동일한 일반 구조를 사용하지만 NOT EXISTS결과에 공급자 'S1'을 포함하지만 잘못 포함합니다 (예 : 수량이 null 인 경우).

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1', NULL ), 
                       ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT DISTINCT spx.sno
  FROM sp spx
 WHERE spx.pno = 'P1'
       AND NOT EXISTS (
                       SELECT *
                         FROM sp spy
                        WHERE spy.sno = spx.sno
                              AND spy.pno = 'P1'
                              AND spy.qty = 1000
                      );

그래서 NOT EXISTS그것이 나타날 수도있는 은색 총알이 아닙니다!

물론 문제의 원인은 null이 존재하므로 '실제'해결책은 이러한 null을 제거하는 것입니다.

이것은 두 가지 테이블을 사용하여 (다른 가능한 디자인 중에서도) 달성 할 수 있습니다.

  • sp 부품 공급으로 알려진 공급 업체
  • spq 알려진 수량으로 부품을 공급하는 것으로 알려진 공급 업체

spq참조 할 경우 외래 키 제약 조건이 있어야합니다 sp.

그런 다음 '빼기'관계 연산자 ( EXCEPT표준 SQL 에서 키워드)를 사용하여 결과를 얻을 수 있습니다.

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1' ), 
                       ( 'S2', 'P1' ),
                       ( 'S3', 'P1' ) )
              AS T ( sno, pno )
     ),
     spq AS 
     ( SELECT * 
         FROM ( VALUES ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT sno
  FROM spq
 WHERE pno = 'P1'
EXCEPT 
SELECT sno
  FROM spq
 WHERE pno = 'P1'
       AND qty = 1000;

Null은 데이터의 의미와 부재, 즉 데이터 값이 아닌 알 수 없음을 나타냅니다. 포인터를 사용할 때 C 유형 언어에서는 null이 실제로 아무것도 아니기 때문에 프로그래밍 배경을 가진 사람들이 이것을 혼동하기가 매우 쉽습니다.

따라서 첫 번째 경우 3은 실제로 (1,2,3, null) 세트에 있으므로 true가 리턴됩니다.

그러나 두 번째로 당신은 그것을 줄일 수 있습니다

3이 아닌 ( 'null)'true '를 선택하십시오

따라서 파서는 비교할 집합에 대해 아무것도 모르기 때문에 아무것도 반환되지 않습니다. 빈 집합이 아니라 알 수없는 집합입니다. (1,2) 집합이 분명히 거짓이기 때문에 (1, 2, null)을 사용하는 것이 도움이되지 않지만 알 수없는 알 수없는 것에 대해 당신이하고 있습니다.


하위 쿼리 포함 NULL에 대해 NOT IN으로 필터링하려면 null이 아닌지 확인하십시오.

SELECT blah FROM t WHERE blah NOT IN
        (SELECT someotherBlah FROM t2 WHERE someotherBlah IS NOT NULL )

이것은 소년을위한 것입니다 :

select party_code 
from abc as a
where party_code not in (select party_code 
                         from xyz 
                         where party_code = a.party_code);

이것은 ansi 설정에 관계없이 작동합니다


또한 조인, 존재 및 http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx 의 논리적 차이점을 알기 위해 사용할 수 있습니다 .

참고 URL : https://stackoverflow.com/questions/129077/not-in-clause-and-null-values

반응형