Оракул отличный, делающий сортировку

Я получаю плохую производительность от DISTINCT. План объяснения показывает, что он выполняет SORT (GROUP BY), который звучит неправильно. Я ожидал бы, что какая-то агрегация HASH даст гораздо лучший результат. Есть ли намек на то, чтобы сказать оракулу использовать HASH для DISTINCT, а не сортировать? Я использовал /* + USE_HASH_AGGREGATION */в аналогичных ситуациях, но он не работает для DISTINCT.

Итак, это мой оригинальный запрос:

SELECT
count(distinct userid) n, col
FROM users
GROUP BY col;

пользователи имеют 30M строк, каждый пользовательский идентификатор 12 раз. Этот запрос занимает 70 секунд.

Теперь мы перепишем его как

SELECT
count(userid) n, col
FROM
(SELECT distinct userid, col FROM users)
GROUP BY col

И это занимает 40 секунд. Теперь добавьте подсказку вместо хэша:

SELECT
count(userid) n, col
FROM
(SELECT /*+ USE_HASH_AGGREGATION */ distinct userid, col FROM users)
GROUP BY col

и требуется 10 секунд.

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

Чертеж: 1) Медленно:

----------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation      | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem | Used-Tmp|
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |               |      1 |        |      5 |00:01:12.01 |     283K|    292K|       |       |      |     |
|   1 |  SORT GROUP BY     |               |      1 |      5 |      5 |00:01:12.01 |     283K|    292K|   194M|   448K|  172M (0)|   73728 |
|   2 |   TABLE ACCESS FULL| USERS |      1 |     29M|     29M|00:00:08.17 |     283K|    283K|       |       |      |     |

2) Быстрый

--------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation        | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |               |      1 |        |      5 |00:00:13.09 |     283K|    283K|   |   |      |
|   1 |  SORT GROUP BY       |               |      1 |      5 |      5 |00:00:13.09 |     283K|    283K|  3072 |  3072 | 2048  (0)|
|   2 |   VIEW               |               |      1 |   8647K|   2445K|00:00:13.16 |     283K|    283K|   |   |      |
|   3 |    HASH UNIQUE       |               |      1 |   8647K|   2445K|00:00:12.57 |     283K|    283K|   113M|    10M|  216M (0)|
|   4 |     TABLE ACCESS FULL| USERS         |      1 |     29M|     29M|00:00:07.68 |     283K|    283K|   |   |      |
--------------------------------------------------------------------------------------------------------------------------------------------

Ответ 1

Как пробовать следующее: Если у вас есть указатель на col и userid, он должен полностью разрешаться в индексе и вообще не нужно прикасаться к таблице.

Select count(userid) n, col
from (select col, userid from users group by col, userid)
group by col
;