Проблема производительности oracle при подсчете()

Я использую 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! В чем разница между этими 2 запросами?

вот план объяснения для обоих запросов:

первый запрос:

----------------------------------------------------------------------------------------------------------
| 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+ (например, первый запустите или измените идентификатор пользователя), иначе это будет стоить

когда я выполнил второй запрос, я всегда могу получить результат в 1s. Поэтому я думаю, что второй лучше, но я не знаю, почему он сильно улучшается.

3 ответов


вы можете увидеть, где разница приходит, сравнивая строку в планах выполнения, которые обращаются к 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. Это на много порядков меньше, поэтому вы видите, что второй запрос выполняется быстрее.

что касается того, почему второе выполнение "медленного" запроса является "быстрым", вы думаете, что это правильно - данные были загружены с диска в буферный кэш, поэтому доступ намного быстрее.


скорее всего, это просто разница в оценке, и у них будет одинаковая статистика выполнения. Трассировка обоих + tkprof для получения реальных данных. Также, если вы хотите получить более подробную информацию о логике оптимизатора-сделайте жесткий анализ с событием 10053.


стоимость - это не только фактор для запросов, иногда это зависит от сервера, который u R показывает, что это стоимость процессора или стоимость ввода-вывода, несколько раз стоимость моя варьируется из-за мощности столбца, условий запроса. если вы хотите увидеть много разъяснений по запросам, получите план объяснения или TKPROOF, чтобы вы узнали , что он собирается для полного сканирования таблицы или какой индекс собирает и время выполнения.