Разница между: NOT IN (НЕ NULL) ПРОТИВ IN (IS NULL) - PullRequest
0 голосов
/ 14 февраля 2019

Я пытаюсь написать запрос с указанием идентификатора и названий отделов, в которых нет сотрудников.Я нашел запрос, который работает, но я не понимаю, почему мой подход не работает.

Оба запроса одинаковы, за исключением подзапроса внутри предложения WHERE.

Мой подход НЕ РАБОТАЕТ: IN ... НЕДЕЙСТВИТЕЛЕН (РЕДАКТИРОВАТЬ: НЕ ВОЗВРАЩАЕТ НИКАКИХ ЗНАЧЕНИЙ)

SELECT
    department_id,
    department_name
FROM
    departments
WHERE
    department_id IN ( 
        SELECT
            department_id
        FROM
            employees
        WHERE
            department_id IS NULL
    )
ORDER BY
    department_name;

РАБОЧИЙ подход: НЕ В ... НЕТ НУЛЬ

SELECT
    department_id,
    department_name
FROM
    departments
WHERE
    department_id NOT IN ( --why -> IN... IS NULL is not working?
        SELECT
            department_id
        FROM
            employees
        WHERE
            department_id IS NOT NULL
    )
ORDER BY
    department_name;

Ответы [ 7 ]

0 голосов
/ 14 февраля 2019

Если мы на мгновение забудем о is null, давайте рассмотрим логику вашего первого запроса:

SELECT
    department_id,
    department_name
FROM
    departments
WHERE
    department_id IN ( 
        SELECT
            department_id
        FROM
            employees
    )
ORDER BY
    department_name;

Возвращает набор отделов, в которых есть сотрудники.По определению результат ограничен множеством employees.department_id.Повторное представление where department_id is null в подзапросе ограничивает значение NULL или создает пустой набор.

Проще говоря, мы не можем вызвать в воображении набор значений, которых нет в таблице, запросив только эту таблицу.Следовательно, мы должны использовать NOT IN (или NOT EXISTS, если таблица в подзапросе содержит нулевые записи) или решение OUTER JOIN, предложенное другими.

0 голосов
/ 14 февраля 2019

Это выражение: X IN ( a,b,c ) эквивалентно: X = a OR X = b OR X = c

Это выражение: NOT X IN ( a,b,c ) эквивалентно: NOT (X = a OR X = b OR X = c), что, в свою очередь, эквивалентно: NOT X = a AND NOT X = b AND NOT X = c

Вам также необходимо изучить Сравнения с NULL и трехзначной логикой (3VL)

Если вы знаете вышеупомянутое, вы можете создать таблицу истинности длядля каждого из выражений и для каждой комбинации значений - это поможет вам понять поведение IN / NOT IN в запросах SQL:

+------+------+---+---+--------------+------------------+
|  X   |  a   | b | c | X in (a,b,c) | NOT X in (a,b,c) |
+------+------+---+---+--------------+------------------+
| 1    | 0    | 1 | 2 | true         | false            |
| 1    | NULL | 1 | 2 | false        | NULL(false) *    |
| 1    | 0    | 2 | 3 | false        | true             |
| 1    | NULL | 2 | 3 | false        | NULL(false) *    |
| NULL | 0    | 1 | 2 | NULL(false)  | NULL(false) *    |
| NULL | NULL | 1 | 2 | NULL(false)  | NULL(false) *    |
+------+------+---+---+--------------+------------------+

Пожалуйста, обратите особое внимание на строки, отмеченные *- это те, которые не соответствуют здравому смыслу, где, если X = false, тогда NOT X должно быть истинным

0 голосов
/ 14 февраля 2019

Вы можете использовать LEFT JOIN с условием WHERE ... IS NULL в предложении WHERE следующим образом:

SELECT
    d.department_id,
    d.department_name
FROM
    departments d
    LEFT JOIN employees e 
        ON e.department_id = d.department_id
WHERE e.department_id IS NULL
0 голосов
/ 14 февраля 2019

Здесь вам нужно: WHERE NOT EXISTS, поскольку вам нужны все отделы, в которых не существует ни одного сотрудника.

select department_id, department_name from departments 
where not exists (
  select 1 from employees where employees.department_id = departments.department_id
)

использование в этом случае нулевого / ненулевого значения неверно в обоих случаях здесь.

0 голосов
/ 14 февраля 2019

NULL == NULL никогда не соответствует действительности.Таким образом, вы спрашиваете, входит ли department_id в набор department_id s, где department_id равно NULL.Это никогда не будет правдой.Во втором запросе вы ищете department_id s, которых нет в списке department_id s, где department_id НЕ NULL.Это даст вам как NULL department_id s, так и department_id s, которых нет в таблице employees.

Я бы отметил, что ваше предложение WHERE в подзапросе не нужно:

WHERE
department_id NOT IN (
    SELECT department_id FROM employees
)
0 голосов
/ 14 февраля 2019

Я бы использовал NOT EXISTS вместо:

SELECT d.*
FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.department_id);

Если подзапрос возвращает null, тогда это выражение рассматривается как false.

Итак, value = NULL будет оцениваться как NULL или UNKNOWN, так что вместо этого вы можете использовать NOT EXISTS.

0 голосов
/ 14 февраля 2019

в вашем первом запросе внутри подзапроса

 SELECT  department_id
        FROM
            employees
        WHERE
            department_id IS NULL

Он не возвращает никаких department_id, поэтому ваш вывод будет нулевым

Оператор in сделает col = val1 илиcol = val2 или col = val3.Помещение нуля туда приведет к col = null, который не будет работать

, в результате в случае оператора in это хорошо отфильтровать ноль

 SELECT  department_id
            FROM
                employees
            WHERE
                department_id IS not NULL --filter out null

В вашем2-й запрос

SELECT
    department_id,
    department_name
FROM
    departments
WHERE
    department_id NOT IN ( --why -> IN... IS NULL is not working?
        SELECT
            department_id
        FROM
            employees
        WHERE
            department_id IS NOT NULL -- null checking is good 
    )
ORDER BY
    department_name;

в подзапросе, который вы отметили. ГДЕ департамент НЕ НЕДЕЙСТВИТЕЛЕН, что защищает вас от выполнения типа col = null, и эти department_id возврат в подзапросе будут отфильтрованы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...