2013-01-05 31 views
6

我使用的是Oracle 11g,主表有大约10m条记录。这里是我的查询:COUNT上的oracle性能问题()

SELECT COUNT (*) 
    FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS 
WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1; 

成本是3736,但是当我把它改成这种形式:

SELECT COUNT (*) FROM 
    (SELECT 1 FROM CONTACT c INNER JOIN STATUS S ON C.STATUS = S.STATUS 
    WHERE C.USER = 1 AND S.REQUIRE = 1 AND ROWNUM = 1); 

的成本达到5!这两个查询有什么区别?

下面是两个查询解释计划:

第一查询:

---------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |       |  1 | 10 | 3736 (1)| 00:00:45 | 
| 1 | SORT AGGREGATE    |       |  1 | 10 |   |   | 
|* 2 | COUNT STOPKEY    |       |  |  |   |   | 
| 3 | NESTED LOOPS    |       | 4627 | 46270 | 3736 (1)| 00:00:45 | 
| 4 |  TABLE ACCESS BY INDEX ROWID| CONTACT     | 6610 | 33050 | 3736 (1)| 00:00:45 | 
|* 5 |  INDEX RANGE SCAN   | IX_CONTACT_USR   | 6610 |  | 20 (0)| 00:00:01 | 
|* 6 |  INDEX RANGE SCAN   | IX_CONTACT_STATUS  |  1 |  5 |  0 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter(ROWNUM=1) 
    5 - access("C"."USER"=1) 
    6 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1) 

第二个查询:

----------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |       |  1 |  |  5 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE     |       |  1 |  |   |   | 
| 2 | VIEW       |       |  1 |  |  5 (0)| 00:00:01 | 
|* 3 | COUNT STOPKEY    |       |  |  |   |   | 
| 4 |  NESTED LOOPS    |       |  2 | 20 |  5 (0)| 00:00:01 | 
| 5 |  TABLE ACCESS BY INDEX ROWID| CONTACT     |  3 | 15 |  5 (0)| 00:00:01 | 
|* 6 |  INDEX RANGE SCAN   | IX_CONTACT_USR   | 6610 |  |  3 (0)| 00:00:01 | 
|* 7 |  INDEX RANGE SCAN   | IX_CONTACT_STATUS  |  1 |  5 |  0 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - filter(ROWNUM=1) 
    6 - access("C"."USER"=1) 
    7 - access("C"."STATUS"="S"."STATUS" AND "S"."REQUIRE"=1) 

我执行2次的查询,第一个有时成本45S + (例如第一次运行或更改用户标识),否则将花费< 1秒。我完全不知道为什么它是如此不同,也许数据库缓存?

当我执行第二个查询时,我总是可以在1秒内得到结果。所以我认为第二个更好,但我不知道它为什么会提高很多。

+0

评论解释计划提供:如我所料,你的两个querries将不会有所不同。唯一不同的是第2行 - 这不会影响性能......(注意解释计划并不总是你在运行时会得到的 - 这就是为什么我建议追踪它。 – igr

+0

好问题,并且肯定会得到不同的答案!我很高兴你投入的计划不是很多,这是令人沮丧的,再加上1就是这样! – glh

+0

您可以为这两个查询发布解释计划吗?它将帮助我们更好地理解表格统计信息并比较两个查询。为获得查询的解释计划,请按照以下链接:http://www.oraclebin.com/2012/12/explain-plan-how-to-get-execution-plan.html –

回答

1

通过比较执行计划中访问CONTACT表的行(查看行列,第一行),可以看到差异在哪里。

第一:

| 4 |  TABLE ACCESS BY INDEX ROWID| CONTACT     | 6610 | 33050 | 3736 (1)| 00:00:45 | 

二:

| 5 |  TABLE ACCESS BY INDEX ROWID| CONTACT     |  3 | 15 |  5 (0)| 00:00:01 | 

在第一个例子中,ROWNUM = 1谓语不适用,直到CONTACT表已被访问后,让你得到6610行从这张桌子返回。而您的第二个查询优化器只返回3。这比以前少了许多个数量级,这就是为什么你会更快地看到第二个查询完成。至于为什么“慢”查询的第二次执行是“快”,你认为是正确的 - 数据已经从磁盘加载到缓冲区缓存中,因此访问速度更快。

+0

我相信这只是显示优化器的估计如何变化。谓词信息部分意味着在两个表之后应用'filter(ROWNUM = 1)'。 –

+0

你是对的,它在两种情况下都使用过滤器,解释计划只是一个估计。为了确保我们需要看到来自SQL跟踪的输出,但是海报的经验性测试似乎证实了计划中显示的内容:第一个查询从第二个“CONTACT”表中访问更多行。 –

+2

我已经运行了一些测试,并且解释计划与问题中显示的内容相匹配,但tkprof结果显示它始终只访问两个查询中两个表中的一行。因此,这种效果可能完全取决于缓存,并且在查询之间(实际上)没有区别 –

1

最有可能的只是估计差异,他们将有相同的执行统计。跟踪两个+ tkprof获取真实数据。 此外,如果你想要优化器逻辑背后的一些更多的细节 - 做事件10053硬解析。

1

成本不仅是查询的因素,有时它也取决于服务器也,你显示它是一个CPU成本或I/O成本,由于列基数,查询的条件,有时候会花费我的差异。如果你想查看关于查询的很多说明,可以得到解释计划或TKPROOF,这样你就会知道,它将进行全表扫描,或者哪个索引正在执行并执行时间。