Отказ от ответственности: я понял проблему (я думаю), но я хотел добавить эту проблему в Stack Overflow, поскольку я не мог (легко) найти ее где-нибудь. Кроме того, у кого-то может быть лучший ответ, чем у меня.
У меня есть база данных, в которой на одну таблицу «Common» ссылаются несколько других таблиц. Я хотел увидеть, какие записи в общей таблице остались без внимания (т. Е. Не имели ссылок ни из одной из других таблиц).
Я выполнил этот запрос:
select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)
Я знаю, что есть потерянные записи, но никаких записей не было возвращено. Почему нет?
(Это SQL Server, если это важно.)
sql
sql-server
tsql
Джереми Штайн
источник
источник
Ответы:
Обновить:
В этих статьях моего блога более подробно описаны различия между методами:
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:SQL Server
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:PostgreSQL
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:Oracle
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:MySQL
Есть три способа выполнить такой запрос:
LEFT JOIN / IS NULL
:NOT EXISTS
:NOT IN
:Когда
table1.common_id
не допускает значения NULL, все эти запросы семантически одинаковы.Когда он допускает значение NULL,
NOT IN
это другое, посколькуIN
(и, следовательно,NOT IN
) возвращается,NULL
когда значение не соответствует чему-либо в списке, содержащемNULL
.Это может сбивать с толку, но может стать более очевидным, если мы вспомним альтернативный синтаксис для этого:
Результатом этого условия является логический продукт всех сравнений в списке. Конечно, одно
NULL
значение даетNULL
результат, который также отображает весь результатNULL
.Мы никогда не можем однозначно сказать, что
common_id
не равно чему-либо из этого списка, поскольку есть хотя бы одно из значенийNULL
.Допустим, у нас есть эти данные:
LEFT JOIN / IS NULL
иNOT EXISTS
вернется3
, ничего неNOT IN
вернет (так как всегда будет оцениваться как или ).FALSE
NULL
В
MySQL
случае столбца, не допускающего значения NULL,LEFT JOIN / IS NULL
иNOT IN
они немного (на несколько процентов) более эффективны, чемNOT EXISTS
. Если столбец допускает значение NULL,NOT EXISTS
это наиболее эффективно (опять же, не очень).В
Oracle
все три запроса дают одинаковые планы (anANTI JOIN
).In
SQL Server
,NOT IN
/NOT EXISTS
более эффективны, так какLEFT JOIN / IS NULL
не могут быть оптимизированыANTI JOIN
его оптимизатором.В
PostgreSQL
,LEFT JOIN / IS NULL
иNOT EXISTS
являются более эффективными , чемNOT IN
, синус они оптимизированы дляAnti Join
, в то время какNOT IN
использованиеhashed subplan
(или даже простой ,subplan
если подзапрос является слишком большим , чтобы хэш)источник
NOT EXISTS
оценивается как ИСТИНА, если запрос внутри него возвращает какие-либо строки.SELECT NULL
может бытьSELECT *
илиSELECT 1
что-то еще,NOT EXISTS
предикат не смотрит на значения строк, а только подсчитывает их.Если вы хотите, чтобы мир был двузначным логическим местом, вы должны сами предотвратить случай null (третье значение).
Не пишите предложения IN, разрешающие нули на стороне списка. Отфильтруйте их!
источник
common_id not in
, мы все еще можем иметьcommon_id
значение, которое естьNULL
. Так не остается ли проблема отсутствия результатов?Таблица1 или Таблица2 имеет несколько нулевых значений для common_id. Вместо этого используйте этот запрос:
источник
источник
Просто с головы до ног ...
Я провел несколько тестов, и вот мои результаты с ответом @ patmortech и комментариями @rexem.
Если Table1 или Table2 не проиндексированы по commonID, вы получаете сканирование таблицы, но запрос @patmortech по-прежнему выполняется вдвое быстрее (для главной таблицы со 100 КБ строк).
Если ни один из них не проиндексирован по commonID, вы получите два сканирования таблицы, и разница будет незначительной.
Если оба индексируются по commonID, запрос «не существует» выполняется в 1/3 времени.
источник
источник
Предположим, эти значения для common_id:
Мы хотим, чтобы возвращалась строка в Common, потому что она не существует ни в одной из других таблиц. Однако ноль бросает вызов гаечному ключу.
С этими значениями запрос эквивалентен:
Это эквивалентно:
Вот здесь и начинается проблема. При сравнении с нулем ответ неизвестен . Таким образом, запрос сводится к
ложно или неизвестно неизвестно:
true и not unkown также неизвестно:
Условие where не возвращает записи, результат которых неизвестен, поэтому мы не возвращаем записи.
Один из способов справиться с этим - использовать оператор exists, а не in. Exists никогда не возвращает unkown, потому что он работает со строками, а не со столбцами. (Строка либо существует, либо нет; нет этой неопределенности с нулевым значением на уровне строки!)
источник
это сработало для меня :)
источник
NOT IN
выступят?источник
У меня был пример, когда я искал, и поскольку одна таблица содержала значение как double, а другая как строку, они не совпадали (или не соответствовали без приведения). Но только НЕ . Как SELECT ... IN ... работал. Странно, но подумал, что поделюсь, если кто-нибудь еще столкнется с этим простым исправлением.
источник
Следуйте приведенному ниже примеру, чтобы понять тему выше:
Также вы можете перейти по следующей ссылке, чтобы узнать Anti-присоединиться
Но если мы используем
NOT IN
в этом случае, мы не получим никаких данных.Это происходит, поскольку (
select department_id from hr.employees
) возвращает нулевое значение, и весь запрос оценивается как ложный. Мы можем увидеть это, если немного изменим SQL, как показано ниже, и обработаем нулевые значения с помощью функции NVL.Теперь получаем данные:
Мы снова получаем данные, поскольку обработали нулевое значение с помощью функции NVL.
источник