【问题标题】:Oracle avg in group by not sorting properlyOracle avg in group by 未正确排序
【发布时间】:2019-01-04 19:52:56
【问题描述】:

在 Oracle 12.2 的 group by 子句中的聚合函数上使用 order by 时,我们收到不正确的结果排序。在玩弄它之后,我们发现该查询仅在以某些方式表达时才有效(见下文)。

这是我们的问题:

1) 为什么按 avg 进行排序很挑剔?查询是否基于某些记录的逻辑/限制按预期运行?是否与底层数据类型为 number(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:按别名转换顺序的平均函数

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:在外部查询中带/不带强制转换的平均函数

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

【问题讨论】:

  • 您的错误结果完全是错误的(假设查询是准确的)。这似乎是您正在使用的 Oracle 版本中的一个错误。
  • 对我来说也像一个错误。前提是,只有一个表有一个名为duration 的列,并且两个表中没有一个名为avg 的列
  • 查看每个查询的执行计划,这可能会显示优化器执行的转换可能在逻辑上是错误的,也可能不是。无论如何 - 您能否发布一个最小的数据样本(您的两个表,列尽可能少,同时仍然能够重新创建您报告的错误行为)?
  • 确实是个bug。它在优化器决定使用 VW_GBC_5 时显示。请参阅下面的答案。

标签: sql oracle oracle12c


【解决方案1】:

我猜为了获得正确的输出结果,您需要已经完成聚合,但是这里发生的情况是聚合完成后再次按顺序进行聚合,因此即使聚合已经完成,这也会再次开始聚合它使用外部查询的别名是最有效的,首先在聚合后完成输出,然后在外部查询中进行排序,如select col1,col2 from (select col,agg(..) from table group by col) order by col2 这将限制为仅对接收到的输出进行排序,而不是再次聚合和排序。

【讨论】:

  • 智能 RDBM 只会计算一次聚合。
【解决方案2】:

我们已将此追溯到我们认为是 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. 切换到不同的优化器版本

    改变会话集 optimizer_features_enable='12.1.0.2';

  4. 使用 no_place_group_by 优化器提示

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-15
    • 2017-06-25
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多