2014-04-24 52 views
2

我有2个表,每个表有大约230000条记录。当我查询时:oracle中没有查询需要比查询中更长的时间

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

其更好的用户不存在,如不使用行搜索的需要太长时间

+0

这绝不是一些黄金法则,可以应用于任何使用NOT IN的查询。 –

2

这是扫描和寻道的区别。 当你要求“IN”时,你特别要求这些值。 这意味着数据库引擎可以使用索引来查找正确的数据页面。

当您询问“NOT IN”时,您会询问除这些值以外的所有值。 这意味着数据库引擎必须扫描整个表/索引才能找到所有值。

另一个因素是数据量。 IN查询可能涉及的数据少得多,因此I/O比NOT IN少得多。

将它与电话簿相比较,如果您希望只有名为Smith的人可以选择Smith的部分并返回该部分。您不必阅读本书之前的任何页面,也不必阅读Smith部分之后的任何页面。 如果你要求所有非史密斯 - 你必须阅读史密斯之前和史密斯之后的所有页面。 这说明了查找/扫描方面和数据量方面。

0

在最坏的情况下,两个查询都可以使用两个全表扫描加散列连接(半或反)来解决。除非你的情况出现异常情况,否则我们会说230,000行。

我的猜测是field1field2是可以为空的。当您使用NOT IN构造时,Oracle必须执行昂贵的过滤器操作,该操作基本上为外部表中的每一行执行一次内部查询。这是230 000全表扫描...

您可以通过查看执行计划来验证这一点。它看起来是这样的:

SELECT 
    FILTER (NOT EXISTS SELECT 0...) 
     TABLE ACCESS FULL ... 
     TABLE ACCESS FULL ... 

如果在任一列中没有NULL值(FIELD1,FIELD2)可以帮助甲骨文与此资料片等都可以用另一种更高效的执行策略:

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 INNOT EXISTS可以更改查询的结果。看看下面的例子,并尝试两种不同地方的子句来看到了差距:

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) 
0

尝试:

SELECT count(*) 
    FROM table1 t1 
LEFT JOIN table2 t2 ON t1.field1 = t2.field2 
    WHERE t2.primary_key IS NULL