6

我使用的是Oracle 11g,主表有大约10m 条记录。这是我的查询:

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 次查询,第一次有时会花费 45 秒以上(例如第一次运行或更改用户 ID),否则会花费 <1 秒。我完全不知道为什么它如此不同,也许是数据库缓存?

当我执行第二个查询时,我总是可以在 1 秒内得到结果。所以我认为第二个更好,但我不知道它改进很多的原因。

4

3 回答 3

1

很可能这只是估计差异,它们将具有相同的执行统计信息。Trace both + tkprof 以获取真实数据。此外,如果您想要优化器逻辑背后的更多细节 - 使用事件 10053 进行硬解析。

于 2013-01-05T06:28:19.177 回答
1

成本不仅是查询的因素,有时它还取决于服务器,您显示的是 CPU 成本还是 I/O 成本,有时成本会有所不同,因为查询的列基数和条件。如果您想了解更多关于查询的说明,请获取解释计划或 TKPROOF,以便您了解,它将用于全表扫描或正在拾取的索引和执行时间。

于 2013-01-05T06:43:55.207 回答
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. 这要少几个数量级,这就是为什么您看到第二个查询完成得更快的原因。

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

于 2013-01-05T16:09:26.977 回答