NOT IN의 하위 쿼리에 NULL이 있으면 전체 SQL 결과가 항상 비어있는 이유

13843 단어 SQL

소개


  • INEXISTS 로 다시 쓰는 것은 성능 튜닝 기법으로 자주 수행됩니다.
  • 이것은 문제없는 동등한 변환입니다

  • 그러나 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개 노크 실시 중

  • 좋은 웹페이지 즐겨찾기