COUNT()上的oracle性能问题

Eri*_*ric 6 sql oracle plsql

我使用的是Oracle 11g,主表有大约1000万条记录.这是我的查询:

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;
Run Code Online (Sandbox Code Playgroud)

成本是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);
Run Code Online (Sandbox Code Playgroud)

成本变为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)
Run Code Online (Sandbox Code Playgroud)

第二个查询:

-----------------------------------------------------------------------------------------------------------
| 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)
Run Code Online (Sandbox Code Playgroud)

我执行了2个查询,第一个查询有时需要45s +(例如首次运行或更改用户ID),否则将花费<1s.我完全不知道为什么会有这样的不同,也许db缓存?

当我执行第二个查询时,我总能得到1s的结果.所以我认为第二个更好,但我不知道它为什么会改善很多.

Chr*_*xon 1

CONTACT您可以通过比较访问该表的执行计划中的行(查看行列,第一个列)来了解差异所在。

第一的:

|   4 |     TABLE ACCESS BY INDEX ROWID| CONTACT                 |  6610 | 33050 |  3736   (1)| 00:00:45 |
Run Code Online (Sandbox Code Playgroud)

第二:

|   5 |      TABLE ACCESS BY INDEX ROWID| CONTACT                 |     3 |    15 |     5   (0)| 00:00:01 |
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,直到访问表ROWNUM = 1之后才应用谓词,因此您将获得从此表返回的行。而在您的第二个查询优化器中仅返回. 这要小很多数量级,这就是为什么您会看到第二个查询完成得更快。CONTACT66103

至于为什么“慢”查询的第二次执行是“快”,您的想法是正确的 - 数据已从磁盘加载到缓冲区缓存中,因此访问速度要快得多。

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