我有2个表,每个表有大约230000条记录。当我进行查询时:
select count(*)
from table1
where field1 in (select field2 from table2).
大概需要0.2 second
。
如果我使用相同的查询,只需将in
更改为not in
select count(*)
from table1
where field1 NOT in (select field2 from table2).
never ends
。
为什么?
答案 0 :(得分:2)
这是扫描和搜索之间的区别。 当你要求“IN”时,你会特别要求这些值。 这意味着数据库引擎可以使用索引来查找正确的数据页。
当您要求“NOT IN”时,您会询问除这些值以外的所有值。 这意味着数据库引擎必须扫描整个表/索引以查找所有值。
另一个因素是数据量。 IN查询可能涉及的数据少得多,因此I / O比NOT IN少得多。
将它与电话簿进行比较,如果您希望只有名为史密斯的人,您可以选择史密斯的部分并将其返回。您不必阅读本书之前的任何页面或史密斯部分之后的任何页面。 如果你要求所有非史密斯 - 你必须阅读史密斯和史密斯之前的所有页面。 这说明了搜索/扫描方面和数据量方面。
答案 1 :(得分:0)
在最坏的情况下,可以使用两个全表扫描和一个散列连接(半连接或反向连接)来解析两个查询。我们正在谈论23万行的几秒钟,除非你的情况有异常情况发生。
我的猜测是field1
或field2
可以为空。当您使用NOT IN
构造时,Oracle必须执行昂贵的过滤操作,该操作基本上对外层表中的每一行执行一次内部查询。这是230 000全表扫描...
您可以通过查看执行计划来验证这一点。它看起来像是:
SELECT
FILTER (NOT EXISTS SELECT 0...)
TABLE ACCESS FULL ...
TABLE ACCESS FULL ...
如果任一列(field1,field2)中没有NULL值,您可以使用这条信息帮助Oracle,以便可以使用另一种更有效的执行策略:
select count(*)
from table1
where field1 is not null
and field1 not in (select field2 from table2 where field2 is not null)
这将生成一个类似于:
的计划SELECT
HASH JOIN ANTI
FULL TABLE SCAN ...
FULL TABLE SCAN ...
...或者您可以将构造更改为NOT EXISTS
(将生成与上面相同的计划):
select count(*)
from table1
where not exists(
select 'x'
from table2
where table2.field2 = table1.field1
);
请注意,从NOT IN
更改为NOT EXISTS
可能会更改查询结果。看看下面的例子并尝试两个不同的where子句来看看差异:
with table1 as(
select 1 as field1 from dual union all
select null as field1 from dual union all
select 2 as field1 from dual
)
,table2 as(
select 1 as field2 from dual union all
select null as field2 from dual union all
select 3 as field2 from dual
)
select *
from table1
--where field1 not in(select field2 from table2)
where not exists(select 'x' from table2 where field1 = field2)
答案 2 :(得分:0)
尝试:
SELECT count(*)
FROM table1 t1
LEFT JOIN table2 t2 ON t1.field1 = t2.field2
WHERE t2.primary_key IS NULL
答案 3 :(得分:0)
用户不存在更好,因为使用行搜索时间不长