절에 있지 않고 널 값이 아닙니다
-
02-07-2019 - |
문제
이 문제는 내가 다른 쿼리라고 생각한 것에 대해 다른 레코드 수를 얻었을 때 발생했습니다. 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 설정에 관계없이 작동합니다