Null и IN () дают неожиданные результаты - PullRequest
6 голосов
/ 29 июля 2011

Это кажется таким простым, я изумлен из-за отсутствия лучшего слова. У меня есть две таблицы, назовем их albums и artists

CREATE TABLE `albums` (
  `album_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `artist_id` bigint(20) DEFAULT NULL,
  `name` varchar(200) NOT NULL,
  PRIMARY KEY (`album_id`)
)
CREATE TABLE `artists` (
  `artist_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(250) NOT NULL,
  PRIMARY KEY (`artist_id`)
)

В каждой таблице несколько сотен тысяч разведок. Некоторые строки альбома имеют нулевое значение artist_id, это ожидается.

Однако, когда я выполняю следующий запрос, чтобы найти исполнителей без альбомов:

SELECT * FROM artists WHERE artist_id NOT IN (SELECT artist_id FROM albums)

... запрос возвращает ноль результатов. Я знаю, что это не так. Итак, я попробовал это:

SELECT * FROM artists WHERE artist_id NOT IN (SELECT artist_id FROM albums WHERE artist_id IS NOT NULL)

... и я вернусь на пару тысяч строк. Мой вопрос: почему первый запрос, похоже, оперирует идеей, что любое число = NULL? Или это странный эффект, который NULL оказывает на оператор IN()? Я чувствую, что это что-то основное, что я пропустил. Я обычно не использую NULL в моих таблицах БД вообще.

Ответы [ 3 ]

7 голосов
/ 29 июля 2011

Вот почему NOT EXISTS семантически правильно

SELECT * FROM artists ar
WHERE  NOT EXISTS
   (SELECT * FROM albums al WHERE ar.artist_id = al.artist_id)

Логика:

  • NOT IN (x, y, NULL) на самом деле
    • NOT (x OR y OR NULL) на самом деле
      • (NOT x) AND (NOT y) AND (NOT NULL)

Так NULL делает недействительным целое NOT IN

7 голосов
/ 29 июля 2011

Быстрый ответ - оператор IN является сокращением для =a OR =b OR .... Если вы включите в этот список нули, то я думаю, что это нарушает утверждение. Ваш второй вариант, вероятно, лучше.

Или использование объединения может также работать и быть более эффективным.

2 голосов
/ 29 июля 2011

Это связано с тем, как интерпретируются значения NULL в SQL. Вы должны думать о них как о НЕИЗВЕСТНОМ значении.

Допустим, у вас есть artist_id = 1

Если вы запустите следующее:

artist_id = NULL

Вместо того, чтобы получить «Ложь» - вы получите «НЕИЗВЕСТНО»;

Когда вы запускаете запрос, такой как ваш, возвращаются только значения, имеющие значение TRUE.

artist_id IN (NULL, NULL, NULL...) = UNKNOWN
artist_id NOT IN (NULL, NULL, NULL....) = UNKNOWN
...