Oracle в группе не сортируется должным образом - PullRequest
0 голосов
/ 04 января 2019

Мы получаем неправильное упорядочение результатов при использовании порядка в агрегатной функции в группе по Oracle 12.2. Поэкспериментировав с этим, мы обнаружили, что запрос работает только тогда, когда он сформулирован определенным образом (см. Ниже).

Итак, вот наши вопросы:

1) Почему упорядочение по avg действует привередливее? Действуют ли запросы, как ожидалось, основываясь на некоторой документированной логике / ограничении? Имеет ли это какое-то отношение к базовому типу данных, являющемуся числом (16) без десятичных знаков?

2) Почему использование псевдонима в запросе 4 заставляет его работать, а запрос 3 не работает?

3) Почему запросы работают лучше при заказе по возрастанию? Это не показано ниже, но запрос 2 работает, когда asc, даже если он не работает desc. Запрос 1 не работает с asc.

В приведенных ниже примерах обратите внимание, что продолжительность - это число (16).

Запрос 1: средний порядок функций по функциям

select
    name,
    avg(duration)
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by avg(duration) desc

-- Query 1 result (wrong)
(some name) 1224417.83471074
(some name) 33568438.1548673
(some name) 3928150.12809406
(some name) 1434939.13464658
(some name) 269338.574638521

Запрос 2: средний порядок функций по псевдониму

-- Query 2: order by avg alias
select
    name,
    avg(duration) avg
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by avg desc

-- Query 2 result (wrong)
-- See query 1 result

-- Note: For some reason this query works correctly when ordered asc

Запрос 3: средняя функция с порядком приведения по функции

select
    name,
    to_number(avg(duration))
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by to_number(avg(duration)) desc

-- Query 3 result (wrong)
-- See query 1 result

Запрос 4: функция Avg с порядком приведения по псевдониму

select
    name,
    to_number(avg(duration)) avg
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by avg desc

-- Query 4 results (correct)
(some name) 562654936
(some name) 498804314
(some name) 263681023
(some name) 245531731
(some name) 188103278
-- the values with decimals show up later in the right order

Запрос 5 и 6: функция Avg с / без приведения с упорядочением во внешнем запросе

select * from (
    select
        name,
        to_number(avg(duration)) avg -- works without to_number as well
    from table1
    join table2 on table1.table2_id = table2.id
    where duration is not null
    group by name
) order by avg desc

-- Query 5 & 6 results (correct)
-- See query 4 results

Ответы [ 2 ]

0 голосов
/ 01 августа 2019

Мы проследили это до того, что мы считаем ошибкой в ​​оптимизаторе Oracle. Это происходит, когда оптимизатор выбирает представление, называемое VW_GBC_5. Соблюдайте планы объяснения ниже:

С выпуском

-- This produces incorrect result set ordering
select 
    /*+ qb_name(m) place_group_by(@m) */ 
    name,
    avg(duration)
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by avg(duration) desc;

-------------------------------------------------------------------------------------------------
| Id  | Operation              | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                | 35540 |  3366K|       |  9433   (1)| 00:00:01 |
|   1 |  SORT ORDER BY         |                | 35540 |  3366K|  3800K|  9433   (1)| 00:00:01 |
|   2 |   HASH GROUP BY        |                | 35540 |  3366K|  3800K|  9433   (1)| 00:00:01 |
|*  3 |    HASH JOIN           |                | 35540 |  3366K|       |  7852   (1)| 00:00:01 |
|   4 |     VIEW               | VW_GBC_5       | 35540 |  1145K|       |  7510   (2)| 00:00:01 |
|   5 |      HASH GROUP BY     |                | 35540 |   416K|       |  7510   (2)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL| TABLE1         |  1225K|    14M|       |  7461   (1)| 00:00:01 |
|   7 |     TABLE ACCESS FULL  | TABLE2         | 38955 |  2434K|       |   342   (1)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Без выдачи

-- This produces correct result set ordering
select 
    /*+ qb_name(m) no_place_group_by(@m) */ 
    name,
    avg(duration)
from table1
join table2 on table1.table2_id = table2.id
where duration is not null
group by name
order by avg(duration) desc;

-----------------------------------------------------------------------------------------------
| Id  | Operation            | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                | 38412 |  2850K|       | 25624   (1)| 00:00:02 |
|   1 |  SORT ORDER BY       |                | 38412 |  2850K|    98M| 25624   (1)| 00:00:02 |
|   2 |   HASH GROUP BY      |                | 38412 |  2850K|    98M| 25624   (1)| 00:00:02 |
|*  3 |    HASH JOIN         |                |  1225K|    88M|  2896K|  9345   (1)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| TABLE2         | 38955 |  2434K|       |   342   (1)| 00:00:01 |
|*  5 |     TABLE ACCESS FULL| TABLE1         |  1225K|    14M|       |  7461   (1)| 00:00:01 |
-----------------------------------------------------------------------------------------------

Обходные

  1. Переписать запрос (см. Оригинальный вопрос)

  2. Отключить _simple_view_merging

    изменить сессионный набор "_simple_view_merging" = false;

  3. Переключиться на другую версию оптимизатора

    alter session set optimizer_features_enable = '12 .1.0.2 ';

  4. Использовать подсказку оптимизатора no_place_group_by

0 голосов
/ 04 января 2019

Я предполагаю, что для правильных результатов вывода вам необходимо выполнить агрегирование, но здесь происходит агрегация, когда она выполняется после повторной группировки, если у вас есть агрегация, так что это снова запускает агрегацию поверх уже выполненной агрегации, даже если его псевдоним, использующий внешний запрос, наиболее эффективен для того, чтобы сначала завершить вывод после агрегирования, а затем иметь порядок во внешнем запросе, такой как select col1,col2 from (select col,agg(..) from table group by col) order by col2. Это ограничит только упорядочение полученных выводов, а не повторное агрегирование и упорядочение.

...