SQL "select where not in subquery" не возвращает результатов
Отказ от ответственности: я выяснил проблему (я думаю), но я хотел добавить эту проблему в 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, если это имеет значение.)
Ответы
Ответ 1
Update:
В этих статьях в моем блоге более подробно описываются различия между методами:
Существует три способа сделать такой запрос:
-
LEFT JOIN / IS NULL
:
SELECT *
FROM common
LEFT JOIN
table1 t1
ON t1.common_id = common.common_id
WHERE t1.common_id IS NULL
-
NOT EXISTS
:
SELECT *
FROM common
WHERE NOT EXISTS
(
SELECT NULL
FROM table1 t1
WHERE t1.common_id = common.common_id
)
-
NOT IN
:
SELECT *
FROM common
WHERE common_id NOT IN
(
SELECT common_id
FROM table1 t1
)
Если table1.common_id
не является нулевым, все эти запросы семантически одинаковы.
Когда он равен нулю, NOT IN
отличается, так как IN
(и, следовательно, NOT IN
) возвращает NULL
, когда значение не соответствует чему-либо в списке, содержащем NULL
.
Это может ввести в заблуждение, но может стать более очевидным, если вспомнить альтернативный синтаксис для этого:
common_id = ANY
(
SELECT common_id
FROM table1 t1
)
Результатом этого условия является булево произведение всех сравнений в списке. Конечно, одно значение NULL
дает результат NULL
, который также отображает весь результат NULL
.
Мы никогда не можем однозначно сказать, что common_id
не соответствует чему-либо из этого списка, так как по крайней мере одно из значений NULL
.
Предположим, что у нас есть эти данные:
common
--
1
3
table1
--
NULL
1
2
LEFT JOIN / IS NULL
и NOT EXISTS
вернется 3
, NOT IN
ничего не вернет (поскольку он всегда будет оценивать либо FALSE
, либо NULL
).
В MySQL
, в случае столбца с непустым значением, LEFT JOIN / IS NULL
и NOT IN
немного (на несколько процентов) эффективнее, чем NOT EXISTS
. Если столбец является нулевым, NOT EXISTS
является наиболее эффективным (опять же, не так много).
В Oracle
все три запроса дают одинаковые планы (a ANTI JOIN
).
В 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
if подзапрос слишком велик для хэша)
Ответ 2
Если вы хотите, чтобы мир был двузначным логическим местом, вы должны предотвратить случай с нулевым (третьим значением).
Не записывайте предложения IN, которые допускают значения NULL в списке. Отфильтруйте их!
common_id not in
(
select common_id from Table1
where common_id is not null
)
Ответ 3
Таблица 1 или Таблица2 имеет некоторые значения null для common_id. Вместо этого используйте этот запрос:
select *
from Common
where common_id not in (select common_id from Table1 where common_id is not null)
and common_id not in (select common_id from Table2 where common_id is not null)
Ответ 4
Просто с головы...
select c.commonID, t1.commonID, t2.commonID
from Common c
left outer join Table1 t1 on t1.commonID = c.commonID
left outer join Table2 t2 on t2.commonID = c.commonID
where t1.commonID is null
and t2.commonID is null
Я провел несколько тестов, и здесь были мои результаты w.r.t. @patmortech answer и комментарии @rexem.
Если таблица Table1 или Table2 не индексируется в commonID, вы получаете сканирование таблицы, но запрос @patmortech по-прежнему в два раза быстрее (для главной таблицы строк 100K).
Если ни один из них не проиндексирован в commonID, вы получаете два сканирования таблицы, и разница незначительна.
Если оба индексируются в commonID, запрос "не существует" выполняется в 1/3 времени.
Ответ 5
SELECT T.common_id
FROM Common T
LEFT JOIN Table1 T1 ON T.common_id = T1.common_id
LEFT JOIN Table2 T2 ON T.common_id = T2.common_id
WHERE T1.common_id IS NULL
AND T2.common_id IS NULL
Ответ 6
select *
from Common c
where not exists (select t1.commonid from table1 t1 where t1.commonid = c.commonid)
and not exists (select t2.commonid from table2 t2 where t2.commonid = c.commonid)
Ответ 7
Предположим, что эти значения для common_id:
Common - 1
Table1 - 2
Table2 - 3, null
Мы хотим, чтобы строка в Common возвращалась, потому что она не существует ни в одной из других таблиц. Однако, нуль бросает в обезьянном гаечном ключе.
С этими значениями запрос эквивалентен:
select *
from Common
where 1 not in (2)
and 1 not in (3, null)
Это эквивалентно:
select *
from Common
where not (1=2)
and not (1=3 or 1=null)
Здесь начинается проблема. При сравнении с нулевым значением ответ неизвестен. Таким образом, запрос сводится к
select *
from Common
where not (false)
and not (false or unkown)
ложно или неизвестно неизвестно:
select *
from Common
where true
and not (unknown)
true, а не unkown также не указано:
select *
from Common
where unknown
Условие where не возвращает записи, в которых результат не указан, поэтому мы не возвращаем записи.
Один из способов справиться с этим - использовать оператор exist, а не in. Exists никогда не возвращается unkown, потому что он работает с строками, а не с столбцами. (Строка либо существует, либо она отсутствует, ни одна из этой нулевой двусмысленности на уровне строк!)
select *
from Common
where not exists (select common_id from Table1 where common_id = Common.common_id)
and not exists (select common_id from Table2 where common_id = Common.common_id)
Ответ 8
это сработало для меня:)
выберите * из Common
где
common_id not in (выберите ISNULL (common_id, 'dummy-data') из таблицы 1)
и common_id не в (выберите ISNULL (common_id, 'dummy-data') из таблицы2)
Ответ 9
select *,
(select COUNT(ID) from ProductMaster where ProductMaster.CatID = CategoryMaster.ID) as coun
from CategoryMaster