NOT IN vs IN Do Not Return Complimentary Results

后端 未结 4 967
灰色年华
灰色年华 2021-02-11 10:03

Hi I am working through example #7 from the sql zoo tutorial: SELECT within SELECT. In the following question

\"Find each country that belongs to a continent where all p

4条回答
  •  猫巷女王i
    2021-02-11 10:49

    Show the table DECLARATION. It seems you use CONTINENT as the continent number. Then you should check it is marked with PRIMARY KEY and NOT NULL options. I realyl suspect you just forgot about very special meaning NULL has in SQL.

    I make an example in Firebird 2.5.1 SQL server.

    CREATE TABLE WORLD (
        CONTINENT   INTEGER,
        NAME        VARCHAR(20),
        POPULATION  INTEGER
    );
    
    
    INSERT INTO WORLD (CONTINENT, NAME, POPULATION) VALUES (NULL, 'null-id', 100);
    INSERT INTO WORLD (CONTINENT, NAME, POPULATION) VALUES (1, 'normal 1', 10);
    INSERT INTO WORLD (CONTINENT, NAME, POPULATION) VALUES (2, 'normal 2', 200);
    INSERT INTO WORLD (CONTINENT, NAME, POPULATION) VALUES (3, 'null-pop', NULL);
    INSERT INTO WORLD (CONTINENT, NAME, POPULATION) VALUES (4, 'normal 4', 110);
    
    COMMIT WORK;
    

    Now let's try your requests and see if the 1st row, having CONTINENT IS NULL would be present anywhere:

    SELECT continent, population FROM world
    WHERE continent IN (
        SELECT continent FROM world
        WHERE population > 100)
    
    CONTINENT   POPULATION
    2           200
    4           110
    

    and then

    SELECT continent, population FROM world
    WHERE continent NOT IN (
        SELECT continent FROM world
        WHERE population > 100)
    
    CONTINENT   POPULATION
    1           10
    3           
    

    By the logic of the request you suppose CONTINENT to be the row ID, then you should make it NOT-NULL and then there would not be the line, that is not seen by [NOT] IN condition.


    Now, let re-phrase this into flat query:

    SELECT continent, population FROM world
        WHERE NOT (population > 100)
    
    CONTINENT   POPULATION
          100
    1           10
    
    SELECT continent, population FROM world
        WHERE population > 100
    
    CONTINENT   POPULATION
    2           200
    4           110
    

    This time the missed row was the one having NULL for Population column.


    Then FreshPrinceOfSO suggested using EXISTS clause. While potentially it may end with most slow (non-effective) query plan, it at least masks away the special meaning of NULL value in SQL.

    SELECT continent, population FROM world w_ext
    WHERE EXISTS (
       SELECT continent FROM world w_int
       WHERE (w_int.population > 100) and (w_int.continent = w_ext.continent)
    )
    
    CONTINENT   POPULATION
    2   200
    4   110
    
    SELECT continent, population FROM world w_ext
    WHERE NOT EXISTS (
       SELECT continent FROM world w_int
       WHERE (w_int.population > 100) and (w_int.continent = w_ext.continent)
    )
    
    CONTINENT   POPULATION
      100
    1   10
    3   
    

提交回复
热议问题