문제

이 문제는 내가 다른 쿼리라고 생각한 것에 대해 다른 레코드 수를 얻었을 때 발생했습니다. not in where 제약과 다른 하나는 a left join. 테이블 not in 제약 조건에는 하나의 널 값 (불량 데이터)이있어 쿼리가 0 레코드의 카운트를 반환했습니다. 나는 왜 그런지 이해하지만 개념을 완전히 이해하는 데 도움이 될 수 있습니다.

간단히 말해서, 왜 쿼리가 결과를 반환합니까? 그러나 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 <> null 알 수 없으므로 술어는 알 수없는 것으로 평가되며 행을 얻지 못합니다.

언제 ansi_nulls 꺼져, 3 <> null 사실이므로 술어는 True로 평가하고 행을 얻습니다.

다른 팁

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

미지의 것은 거짓과 동일하지 않습니다.

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

두 쿼리 모두 결과가 제공되지 않습니다

미지수가 False와 동일하다면 첫 번째 쿼리가 당신에게 false가 당신에게 false를 줄 것이라고 가정하면 두 번째 쿼리는 (false)와 동일했기 때문에 true로 평가해야합니다.
그렇지 않습니다.

아주 좋은 것이 있습니다 SQLServerCentral 의이 주제에 관한 기사.

Nulls와 3 값 논리의 전체 문제는 처음에는 약간 혼란 스러울 수 있지만 TSQL에 올바른 쿼리를 작성하려면 이해해야합니다.

내가 추천 할 또 다른 기사는입니다 SQL 골재 함수 및 null.

NOT IN 알 수없는 값과 비교할 때 0 레코드를 반환합니다

부터 NULL 알려지지 않은 a NOT IN 쿼리가 포함되어 있습니다 NULL 또는 NULL가능한 값 목록에서 s는 항상 반환됩니다. 0 기록은 NULL 값은 테스트중인 값이 아닙니다.

NULL과 비교하여 NULL을 사용하지 않는 한 정의되지 않았습니다.

따라서 3을 NULL (Query A)로 비교하면 정의되지 않은 반환됩니다.

즉, 3 인치 (1,2, null)에서 'true'를 선택하고 'true'를 선택하십시오.

(정의되지 않은)가 아직 정의되지 않았지만 사실이 아니기 때문에 동일한 결과를 생성합니다.

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

SQL은 제약 조건 및 널 값이 아닙니다

질문의 텍스트에서 SQL DML에서 문제가 발생한 것으로 보입니다. SELECT SQL DDL이 아닌 쿼리 CONSTRAINT.

그러나 특히 제목의 문구를 감안할 때, 나는 여기서 제정 된 일부 진술이 잠재적으로 오해의 소지가있는 진술, (paraphrasing)를 따르는 진술이라고 지적하고 싶습니다.

술어가 알 수없는 것으로 평가되면 행이 없습니다.

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

이 매우 간단한 테이블을 고려하십시오. 질문에서 Predicates에서 직접 가져온 두 가지 제약 조건 (@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) 미지로 평가합니다. 하지만, 인서트가 성공합니다! 따라서이 경우 "결과적으로 행을 삽입했기 때문에"당신은 줄을 얻지 못합니다 "라고 말하는 것은 엄격하게 맞지 않습니다.

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

7.6 조항

결과는 검색 조건의 결과가 사실 인 T의 행의 표입니다.

4.10 무결성 제약

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

다시 말해:

SQL DML에서는 결과에서 행이 제거됩니다. WHERE 그것 때문에 알 수없는 것으로 평가합니다 하지 않습니다 조건을 만족시킵니다.

SQL DDL (즉, 제약 조건)에서, 행은 결과에서 제거되지 않아서 알 수 없기 때문에 행이 제거되지 않습니다. 하다 "거짓이 아니다"조건을 만족시킨다.

SQL DML 및 SQL DDL의 영향은 각각 모순되는 것처럼 보일 수 있지만, 알 수없는 결과를 제공하는 실질적인 이유가 있습니다. :이 행동이 없으면 모든 제약은 널을 명시 적으로 처리해야하며 언어 설계 관점에서 매우 불만족 스러울 것입니다 (말할 것도없이 코더에게는 올바른 고통이 있습니다!).

추신 : "알 수없는 제약 조건을 충족시키지 못한다"는 논리를 따르기가 어렵다면, 내가 쓰는 것처럼, SQL DDL과 SQL의 무효 열을 피함으로써 단순히이 모든 것을 분배 할 수 있다고 생각합니다. 널을 생성하는 DML (예 : 외부 조인)!

a에서 3은 세트의 각 구성원에 대한 평등을 테스트하여 양보 (false, false, true, unknown). 요소 중 하나가 참이므로 조건은 참입니다. (일부 단락이 여기에서 발생할 수도 있으므로, 첫 번째 True에 부딪히 자마자 실제로 멈추고 3 = NULL을 평가하지 않습니다.)

B에서는 조건이 아닌 것으로 평가하고 있다고 생각합니다 (3 in (1,2, null)). 세트 수율에 대한 평등을 테스트하는 3 (거짓, 거짓, 알 수없는). (알 수 없음)는 알 수 없습니다. 따라서 전반적으로 조건의 진실은 알려지지 않았으며, 결국에는 본질적으로 거짓으로 취급됩니다.

NULL은 데이터를 의미하고 부재합니다. 즉, 데이터 값이 아닌 것은 알려져 있지 않습니다. Pointers Null을 사용할 때 C 유형 언어에서는 프로그래밍 배경의 사람들이이를 혼동하는 것은 매우 쉽습니다.

따라서 첫 번째 사례에서 3은 실제로 (1,2,3, null) 세트에 있으므로 True가 반환됩니다.

그러나 두 번째에는 그것을 줄일 수 있습니다

3이 아닌 곳 (null)을 선택하십시오.

따라서 파서가 비교하는 세트에 대해 아무것도 모르기 때문에 아무것도 반환되지 않습니다. 빈 세트가 아니라 알려지지 않은 세트입니다. (1, 2, null)를 사용하는 것은 (1,2) 세트가 분명히 거짓이기 때문에 도움이되지 않지만, 당신은 알려지지 않은 미지의 것에 대해 그것을하고 있습니다.

여기에서 답변에서 결론을 내릴 수 있습니다 NOT IN (subquery) 널을 올바르게 처리하지 않으며 NOT EXISTS. 그러나 그러한 결론은 시기상조 일 수 있습니다. Chris Date (Database Programming and Design, Vol 2 No 9, 1989 년 9 월)에 적용되는 다음 시나리오에서는 다음과 같습니다. NOT IN 널을 올바르게 처리하고 올바른 결과를 반환합니다. NOT EXISTS.

테이블을 고려하십시오 sp 공급 업체를 대표하려면 (sno) 부품을 공급하는 것으로 알려진 사람 (pno) 수량 (qty). 테이블은 현재 다음 값을 보유하고 있습니다.

      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'이 잘못 포함되어 있습니다 (즉, 수량이 무효 인) :

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 그것이 나타날 수있는은 총알이 아닙니다!

물론 문제의 원인은 널의 존재이므로 '실제'솔루션은 그 널을 제거하는 것입니다.

이것은 두 개의 테이블을 사용하여 (다른 가능한 설계 중)를 달성 할 수 있습니다.

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

서브 쿼리 오염 된 널로 필터링하고 싶다면 널 nuth에 대한 그냥 체크

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 설정에 관계없이 작동합니다

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top