NOT IN의 하위 쿼리에 NULL이 있으면 전체 SQL 결과가 항상 비어있는 이유
13843 단어 SQL
소개
IN
를 EXISTS
로 다시 쓰는 것은 성능 튜닝 기법으로 자주 수행됩니다.NOT IN
예를 소개합니다.
Schema
CREATE TABLE members (
id INT NOT NULL PRIMARY KEY auto_increment,
name TEXT,
age INT,
city TEXT,
class_name TEXT
);
INSERT INTO members VALUES
(1, 'brown', 22, 'tokyo', 'A'),
(2, 'rally', 19, 'saitama', 'A'),
(3, 'boggie', 21, 'chiba', 'A'),
(4, 'saito', 22, 'tokyo', 'B'),
(5, 'tashiro', NULL, 'tokyo', 'B'),
(6, 'yamada', 18, 'tokyo', 'B'),
(7, 'izumi', 19, 'chiba', 'B'),
(8, 'takeda', 20, 'chiba', 'B'),
(9, 'ishikawa', 19, 'kanagawa', 'B');
id = 5 의 age 가 NULL 인 것을 주목합니다.
Query
추출하려는 조건
「도쿄 거주 클래스 B의 연령군에 포함되지 않는 클래스 A의 멤버」
쓰는 방법은 여러 가지가 있습니다 👇
WITH
members_A AS ( SELECT * FROM members WHERE class_name = 'A')
SELECT * FROM members_A
WHERE age NOT IN (SELECT age FROM members WHERE class_name = 'B' AND city = 'tokyo')
예상 Results
NOT IN의 괄호 안은
NOT EXISTS
그래서 괄호 밖의 나이가 선택된다고 생각하면다음 결과를 기대하십니까?
id
이름
age
도시
class_name
2
rally
19
saitama
A
3
boggie
21
치바
A
하지만 실제로는 달랐습니다.
실제 Results
한 줄도 선택되지 않습니다.
순서를 따라 설명합니다.
1 하위 쿼리를 실행하여 나이 목록을 가져옵니다.
SELECT *
FROM members_A
WHERE age NOT IN (22,NULL,18)
2 NOT IN을 NOT과 IN을 사용하여 동일한 값 변환
SELECT *
FROM members_A
WHERE NOT age IN (22,NULL,18)
3 IN 술어를 OR로 동등 변환
SELECT *
FROM members_A
WHERE NOT ( (age=22) OR (age=NULL) OR (age=18) )
4 도모건의 법칙을 사용한 동치 변환
SELECT *
FROM members_A
WHERE NOT ( (age=22) AND NOT (age=NULL) AND NOT (age=18) )
5 NOT 및 =를 <>로 동등 변환
SELECT *
FROM members_A
WHERE NOT ( (age<>22) AND (age<>NULL) AND (age<>18) )
6 NULL 에 <> 를 적응하면 unknown 가 된다
근거
SELECT *
FROM members_A
WHERE NOT ( (age<>22) AND (unknown) AND (age<>18) )
7 AND 연산에 unknown이 포함되면 true가 되지 않는다
근거
SELECT *
FROM members_A
WHERE false または unknown
결과적으로, 1행도 true가 되지 않습니다(일로 이것에 빠지면, 시간 녹이지요).
따라서 NOT IN의 하위 쿼리에 NULL이 있으면 전체 SQL 결과는 항상 비어 있습니다.
끔찍하네요. .
기대하는 Results를 얻을 수 있는 방법은 다음과 같은 쿼리입니다.
WITH
members_A AS ( SELECT * FROM members WHERE class_name = 'A')
SELECT * FROM members_A
WHERE NOT EXISTS (
SELECT age
FROM members AS members_B
WHERE class_name = 'B'
AND city = 'tokyo'
AND members_B.age = members_A.age
)
이쪽도, 나이가 NULL일 때의 행(id=5)의 평가 프로세스를 단계적으로 쫓아갑니다.
SELECT * FROM members_A
WHERE NOT EXISTS (
SELECT age
FROM members AS members_B
WHERE class_name = 'B'
AND city = 'tokyo'
AND NULL = members_A.age
)
NULL 에 = 를 적응하면 unknown 가 된다
→ AND의 연산에 unknown이 포함되면 true가 되지 않는다
SELECT * FROM members_A
WHERE NOT EXISTS (
SELECT age
FROM members AS members_B
WHERE class_name = 'B'
AND city = 'tokyo'
AND unknown
)
EXISTS 술어는 진위 값만 리턴합니다.
EXISTS 절은 존재 검사라고도하며, 부질의에 의해 반환 된 레코드가 하나이면 참, 하나도 없으면 거짓을 반환합니다.
→ false 또는 unknown이 반환되지만 EXISTS 술어는 진위 값만 반환하므로 false가 반환됩니다.
→ NOT false로 결국 true가 반환됩니다.
SELECT * FROM members_A
WHERE true
최종 결과는 아래.
id
이름
age
도시
class_name
2
rally
19
saitama
A
3
boggie
21
치바
A
IN과 EXISTS는 동일한 값 변환이 가능하지만 NOT IN과 NOT EXISTS는 동일한 값이 아닙니다.
이 현상은 제대로 기억합시다.
참조
69-72p
출력 100개 노크 실시 중
Reference
이 문제에 관하여(NOT IN의 하위 쿼리에 NULL이 있으면 전체 SQL 결과가 항상 비어있는 이유), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/ryosuketter/items/eda1c71abb6a56fe8a6f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)