PostgreSQL 'NOT IN' и подзапрос

89

Я пытаюсь выполнить этот запрос:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (select consols.mac from consols)

Но я не получаю результатов. Я проверил его и знаю, что с синтаксисом что-то не так. В MySQL такой запрос отлично работает. Я добавил строку, чтобы убедиться, что macв consolsтаблице есть строка, которой нет , но она все равно не дает никаких результатов.

сковрон-линия
источник
4
Является ли consols.macстолбец NULLили NOT NULL?
Марк Байерс

Ответы:

167

При использовании NOT IN вы должны убедиться, что ни одно из значений не является NULL:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (
    SELECT mac
    FROM consols
    WHERE mac IS NOT NULL -- add this
)
Марк Байерс
источник
4
Примечание: WHERE mac IS NOT NULLпредложение в подзапросе не требуется, поскольку In(...)всегда удаляет NULL (и дублирует). Потому что набор не может содержать NULL
wildplasser
7
@wildplasser Я не знаю об этом. У меня это не работало, пока я не добавил IS NOT NULL. Вложенный SELECTвозвращал несколько NULLS, и это сбивало с толку IN(SELECT...).
robins35
2
Я был бы очень признателен за объяснение, почему IS NOT NULLэто работает.
mbarkhau
7
Похоже, что использование NULLв NOT INпредложении не работает, потому что сравнение с не NULLявляется ни истинным, ни ложным. sqlbadpractices.com/using-not-in-operator-with-null-values
mbarkhau
2
Запрос не вернет никаких строк при отсутствии, is not nullесли подзапрос не дает совпадающих значений и хотя бы одно nullзначение. Из раздела 9.22 текущего (версия 10) руководства PostgreSQL: «[…] если нет равных правых значений и хотя бы одна правая строка дает ноль, результатом конструкции NOT IN будет NULL, а не истина . "
Кристофер Льюис
29

При использовании NOT IN вы также должны учитывать NOT EXISTS, который молча обрабатывает нулевые случаи. См. Также PostgreSQL Wiki

SELECT mac, creation_date 
FROM logs lo
WHERE logs_type_id=11
AND NOT EXISTS (
  SELECT *
  FROM consols nx
  WHERE nx.mac = lo.mac
  );
Wildplasser
источник
3
Также обратите внимание на огромную потерю производительности при использовании NOT EXISTSvs... NOT IN
IcanDivideBy0
1
@ IcanDivideBy0 В большинстве случаев они генерируют один и тот же план запроса. Вы это проверяли?
wildplasser
1
Также можно повысить производительность при использовании NOT IN вместо NOT EXISTS в случае подзапросов. См. Wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_NOT_IN
Gerbrand
8

Вы также можете использовать условие LEFT JOIN и IS NULL:

SELECT 
  mac, 
  creation_date 
FROM 
  logs
    LEFT JOIN consols ON logs.mac = consols.mac
WHERE 
  logs_type_id=11
AND
  consols.mac IS NULL;

Индекс столбцов «mac» может повысить производительность.

Фрэнк Хайкенс
источник