Last week I’ve mentioned on Twitter that we ran into wrong result bug. We found workaround quickly but I’ve decided to spend some time to reproduce error and write blog post to warn you about this optimizer behavior. 
Special thanks to my colleague who spotted odd results which led us to this finding.

My test (virtual) environment is:
OS: Oracle Enterprise Linux 5.8
DB: Oracle EE 11.1.0.7.12

In test I will use three tables:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CONT
Name    Null Type         
------- ---- -------------
CUST_ID      NUMBER(38)   
CODE         VARCHAR2(100)
 
CUST
Name    Null     Type      
------- -------- ----------
CUST_ID NOT NULL NUMBER(38)
 
DRAG
Name    Null Type     
------- ---- ---------
DRAG_ID      NUMBER(6)

To gather fresh statistics for the tables:

1
2
3
4
5
6
begin
  dbms_stats.gather_table_stats(ownname=>user,tabname=>'CONT',estimate_percent=>100, cascade=>TRUE);
  dbms_stats.gather_table_stats(ownname=>user,tabname=>'CUST',estimate_percent=>100, cascade=>TRUE);
  dbms_stats.gather_table_stats(ownname=>user,tabname=>'DRAG',estimate_percent=>100, cascade=>TRUE);
end;
/

More details about tables:

1
2
3
4
5
6
7
8
9
10
select table_name, num_rows, blocks, partitioned, last_analyzed
from dba_tables
where table_name in ('CONT','CUST','DRAG');
 
 
TABLE_NAME     NUM_ROWS     BLOCKS PARTITIONED LAST_ANALYZED    
------------ ---------- ---------- ----------- -------------------
CONT            1181949       2892 NO          04.02.2014 14:49:24
DRAG                314          5 NO          04.02.2014 14:49:25
CUST             576233        902 NO          04.02.2014 14:49:25

Information about indexes:

1
2
3
4
5
6
7
8
9
select index_name, table_name, uniqueness, distinct_keys, clustering_factor
from dba_indexes
where table_name in ('CONT','CUST','DRAG');
 
 
INDEX_NAME     TABLE_NAME   UNIQUENESS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- ------------ ---------- ------------- -----------------
I_CUST_ID      CONT         NONUNIQUE         468738            753983
PK_CUST_ID     CUST         UNIQUE            576233               878

We have three small and simple tables with just two indexes. CUST table has primary key on “cust_id” column.

After this little introduction it is time for some tests.

I will flush buffer cache and shared pool before every query execution.

1
2
3
4
5
SQL> alter system flush shared_pool;
System altered.
 
SQL> alter system flush buffer_cache;
System altered.

First query execution and execution plan:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
select /*+ gather_plan_statistics */
       count(co.code) as cnt
from drag t,
     cust cus,
     cont co
where 1=1
and t.drag_id = cus.cust_id
and cus.cust_id = co.cust_id(+)
group by t.drag_id;
 
            CNT
---------------
              2
              2
              2
              2
              1
              2
              2
              2
              2
...
303 rows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
 
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------
SQL_ID  gpnrgy2vawafq, child number 0
-------------------------------------
select /*+ gather_plan_statistics */        count(co.code) as cnt from
drag t,      cust cus,      cont co where 1=1 and t.drag_id =
cus.cust_id and cus.cust_id = co.cust_id(+) group by t.drag_id
 
Plan hash value: 3989628059
 
---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |      1 |        |    303 |00:00:00.62 |    3734 |   3724 |       |       |          |
|   1 |  HASH GROUP BY       |      |      1 |    303 |    303 |00:00:00.62 |    3734 |   3724 |  1096K|  1096K| 1264K (0)|
|*  2 |   HASH JOIN OUTER    |      |      1 |    792 |   1084 |00:00:00.16 |    3734 |   3724 |  1206K|  1206K| 1244K (0)|
|*  3 |    HASH JOIN         |      |      1 |    314 |    314 |00:00:00.04 |     890 |    885 |  1452K|  1452K| 1470K (0)|
|   4 |     TABLE ACCESS FULL| DRAG |      1 |    314 |    314 |00:00:00.01 |       7 |      6 |       |       |          |
|   5 |     TABLE ACCESS FULL| CUST |      1 |    576K|    576K|00:00:00.02 |     883 |    879 |       |       |          |
|   6 |    TABLE ACCESS FULL | CONT |      1 |   1181K|   1181K|00:00:00.01 |    2844 |   2839 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("CUS"."CUST_ID"="CO"."CUST_ID")
   3 - access("T"."DRAG_ID"="CUS"."CUST_ID")

Check result of the query - this is correct query result.

Now to simulate what we experienced in production.

1
2
3
4
SQL> alter system flush shared_pool;
System altered.
SQL> alter system flush buffer_cache;
System altered.  

With hint I want to force PK_CUST_ID index usage because this was preferred plan in production.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
select /*+ gather_plan_statistics index(cus PK_CUST_ID) */
       count(co.code) as cnt
from drag t,
     cust cus,
     cont co
where 1=1
and t.drag_id = cus.cust_id
and cus.cust_id = co.cust_id(+)
group by t.drag_id;
 
            CNT
---------------
              0
              0
              0
              0
              0
              0
              0
              0
              0
              0
... 303 rows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
 
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  9vf9uf7mhdmdz, child number 0
-------------------------------------
select /*+ gather_plan_statistics index(cus PK_CUST_ID) */
count(co.code) as cnt from drag t,      cust cus,      cont co where
1=1 and t.drag_id = cus.cust_id and cus.cust_id = co.cust_id(+) group
by t.drag_id
 
Plan hash value: 3263881209
 
-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |            |      1 |        |    303 |00:00:00.70 |    3459 |   3094 |       |       |          |
|   1 |  HASH GROUP BY         |            |      1 |    303 |    303 |00:00:00.70 |    3459 |   3094 |   934K|   934K| 1267K (0)|
|*  2 |   HASH JOIN OUTER      |            |      1 |    764 |   1046 |00:00:00.22 |    3459 |   3094 |  1134K|  1134K| 1198K (0)|
|   3 |    NESTED LOOPS        |            |      1 |    303 |    303 |00:00:02.02 |     615 |    255 |       |       |          |
|   4 |     VIEW               | VW_GBC_9   |      1 |    303 |    303 |00:00:00.01 |       7 |      6 |       |       |          |
|   5 |      HASH GROUP BY     |            |      1 |    303 |    303 |00:00:00.01 |       7 |      6 |  1012K|  1012K| 1249K (0)|
|   6 |       TABLE ACCESS FULL| DRAG       |      1 |    314 |    314 |00:00:00.01 |       7 |      6 |       |       |          |
|*  7 |     INDEX UNIQUE SCAN  | PK_CUST_ID |    303 |      1 |    303 |00:00:00.22 |     608 |    249 |       |       |          |
|   8 |    TABLE ACCESS FULL   | CONT       |      1 |   1181K|   1181K|00:00:00.01 |    2844 |   2839 |       |       |          |
-----------------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("CUS"."CUST_ID"="CO"."CUST_ID")
   7 - access("ITEM_1"="CUS"."CUST_ID")

Check result of the query!
Count is displaying all 0 values because it received only NULLs to count. 
Other functions like max and min are also affected by this error.

Check steps 4,5 and 6 in execution plan.

Instead of quick full scan on DRAG table Oracle transformed query and created inline view using smart group-by optimization.

In 10053 trace I could easily find what Oracle was doing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
  /*+ INDEX ("CUS" "PK_CUST_ID") */
  SUM("VW_GBC_9"."ITEM_2") "CNT"
FROM
  (SELECT "T"."DRAG_ID" "ITEM_1",
    COUNT("CO"."CODE") "ITEM_2",
    "T"."DRAG_ID" "ITEM_3"
  FROM "ADMIN"."DRAG" "T"
  WHERE 1=1
  GROUP BY "T"."DRAG_ID",
    "T"."DRAG_ID"
  ) "VW_GBC_9",
  "ADMIN"."CUST" "CUS",
  "ADMIN"."CONT" "CO"
WHERE "VW_GBC_9"."ITEM_1"="CUS"."CUST_ID"
AND "CUS"."CUST_ID"      ="CO"."CUST_ID"(+)
GROUP BY "VW_GBC_9"."ITEM_3";

Quick workaround to fix this bug:
- Set "_optimizer_group_by_placement"=FALSE.

You could check in 10053 trace value of this parameter.
In my case: _optimizer_group_by_placement = true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SQL> alter session set "_optimizer_group_by_placement"=FALSE;
 
Session altered.
 
select /*+ gather_plan_statistics index(cus PK_CUST_ID) */
       count(co.code) as cnt
from drag t,
     cust cus,
     cont co
where 1=1
and t.drag_id = cus.cust_id
and cus.cust_id = co.cust_id(+)
group by t.drag_id;
 
       CNT
----------
         2
         2
         2
         2
         1
         2
         2
         2
         2
...
303 rows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
 
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------
SQL_ID  a91bzhvupzquh, child number 0
-------------------------------------
select /*+ gather_plan_statistics index(cus PK_CUST_ID)*/
count(co.code) as cnt from drag t,      cust cus,      cont co where
1=1 and t.drag_id = cus.cust_id and cus.cust_id = co.cust_id(+) group
by t.drag_id
 
Plan hash value: 2460166079
 
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |            |      1 |        |    303 |00:00:00.16 |    3481 |       |       |          |
|   1 |  HASH GROUP BY       |            |      1 |    303 |    303 |00:00:00.16 |    3481 |  1096K|  1096K| 1232K (0)|
|*  2 |   HASH JOIN OUTER    |            |      1 |    792 |   1084 |00:00:00.01 |    3481 |  1206K|  1206K| 1529K (0)|
|   3 |    NESTED LOOPS      |            |      1 |    314 |    314 |00:00:00.01 |     637 |       |       |          |
|   4 |     TABLE ACCESS FULL| DRAG       |      1 |    314 |    314 |00:00:00.01 |       7 |       |       |          |
|*  5 |     INDEX UNIQUE SCAN| PK_CUST_ID |    314 |      1 |    314 |00:00:00.01 |     630 |       |       |          |
|   6 |    TABLE ACCESS FULL | CONT       |      1 |   1181K|   1181K|00:00:00.01 |    2844 |       |       |          |
------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("CUS"."CUST_ID"="CO"."CUST_ID")
   5 - access("T"."DRAG_ID"="CUS"."CUST_ID")
 
 
27 rows selected. 

Oracle Support note associated with "_optimizer_group_by_placement" parameter.

Note.8945586.8 Ext/Pub Bug 8945586 - Wrong results using GROUP BY placement:
Description
Wrong results can occur when using GROUP BY placement where the aggregate column gets pruned from select list.

I’ve even found that “_optimizer_group_by_placement” parameter was mentioned in "Oracle® Fusion Middleware Oracle WebCenter Analytics Installation and Upgrade Guide".

Oracle 11g (11.1.0.6 and above) in default or Oracle Real Application Clusters (RAC) configuration

When running Oracle 11g versions prior to 11.1.0.7.0 the Oracle system parameter _optimizer_group_by_placement must be set to false. This can either be set in the init.ora file of the respective database instances or by by issuing an ALTER SYSTEM command as follows:

SQLPLUS /nolog
CONNECT / AS SYSDBA
ALTER SYSTEM SET "_optimizer_group_by_placement"=false

group by的优化bug还是挺多的,还有比如_optimizer_aggr_groupby_elim,但是不出bug的情况下,性能提升还是非常明显的,大家一定要仔细检查结果,不要只看性能。

oracle group by placement可能导致错误结果的bug的更多相关文章

  1. oracle已知会导致错误结果的bug列表(Bug Issues Known to cause Wrong Results)

    LAST UPDATE:     1 Dec 15, 2016 APPLIES TO:     1 2 3 4 Oracle Database - Enterprise Edition - Versi ...

  2. django继承修改 User表导致的问题 fields.E304(permissions/group都会有这样的错误)

    问题: django继承修改 User表时,进行migrations操作时会导致的问题 fields.E304(permissions/group都会有这样的错误)如图: 根源: django文档中有 ...

  3. 登陆Oracle,报oracle initializationg or shutdown in progress 错误提示

    前两天,登陆Oracle,发现登陆不上去了,报”oracle initializationg or shutdown in progress 错误提示” 错误. 然后就想着怎么去解决,首先自己到win ...

  4. oracle:数据库版本问题导致的bug

    公司开发出来的系统,由于各现场oracle数据库版本有10.2.0.4.11.2.0.1.11.2.0.3.11.2.0.4: 进而会导致版本不一导致错误问题.下面列举2个: 1.wm_concat ...

  5. Oracle ORA-01033: ORACLE initialization or shutdown in progress 错误解决办法

    Oracle ORA-01033: ORACLE initialization or shutdown in progress 错误解决办法 登陆数据库时提示 “ORA-01033”错误在命令窗口以s ...

  6. oracle group by中cube和rollup字句的使用方法及区别

    oracle group by中rollup和cube的区别:  Oracle的GROUP BY语句除了最基本的语法外,还支持ROLLUP和CUBE语句. 如果是ROLLUP(A, B, C)的话,先 ...

  7. oracle所在磁盘空间不足导致了数据库异常

    oracle所在磁盘空间不足导致了数据库异常.需要减小数据文件的大小来解决. 1.检查数据文件的名称和编号 select file#,name from v$datafile; 2.看哪个数据文件所占 ...

  8. MVC4 路由参数带点 文件名后缀导致错误

    错误描述 最近在研究office在线预览,用到mvc4  apicontroller 需要传参是文件名,如test.docx导致错误"指定的目录或文件在 Web 服务器上不存在", ...

  9. Oracle问题之ORA-12560TNS:协议适配器错误

    Oracle问题之ORA-12560TNS:协议适配器错误 一.造成ORA-12560: TNS: 协议适配器错误的问题的原因有三个: 1.监听服务没有起起来.windows平台个一如下操作:开始-- ...

随机推荐

  1. Java基础学习心得笔记

    对于很多只会C语言的初学者而言,面对java基础语法学习,反而感觉很难,其实其中最大的问题不是语法难,而是一种编程思想的转变.面向过程就是把你的代码封装成函数,然后依次去做一件事情,面向过程是把你要做 ...

  2. 谢大神给的C++和C# DES加解密代码

    // CPPdesTest.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h"//#include <windows.h>//#inc ...

  3. C# Aspose.Cells导出xlsx格式Excel,打开文件报“Excel 已完成文件级验证和修复。此工作簿的某些部分可能已被修复或丢弃”

    报错信息: 最近打开下载的 Excel,会报如下错误.(xls 格式不受影响) 解决方案: 下载代码(红色为新添代码) public void download() { string fileName ...

  4. react material-ui 添加jss插件

    jss.config.js import { create } from "jss"; import preset from "jss-preset-default&qu ...

  5. vim 多窗口操作

    1.打开多个窗口打开多个窗口的命令以下几个:横向切割窗口:new+窗口名(保存后就是文件名) :split+窗口名,也可以简写为:sp+窗口名纵向切割窗口名:vsplit+窗口名,也可以简写为:vsp ...

  6. ubuntu下openssh升级

    因客户漏扫扫描出来openssh及openssl存在漏洞,现升级为官方最新版 这里选择编译安装 去官网下载: openssl-1.0.2o.tar.gz openssh7.7.1 zlib-1.2.1 ...

  7. Linux命令行下快捷键

    快捷键 说明 Ctrl+a 切换到命令行开始 Ctrl+e 切换到命令行末尾 Ctrl+c 终止当前命令或脚本 Ctrl+d ①退出当前shell,相当于exit②一个个删除光标后字符 Ctrl+l ...

  8. asp.net mvc ef 性能监控调试工具 MiniProfiler

    MiniProfiler是一款针对.NET, Ruby, Go and Node.js的性能分析的轻量级程序.可以对一个页面本身,及该页面通过直接引用.Ajax.Iframe形式访问的其它页面进行监控 ...

  9. js 事件模型

    说到事件,就要追溯到网景与微软的“浏览器大战”了.当时,事件模型还没有标准,两家公司的实现就是事实标准.网景在Navigator中实现了“事件捕获”的事件系统,而微软则在IE中实现了一个基本上相反的事 ...

  10. PE就是市盈率的缩写 PB是平均市净率的缩写

    PE就是市盈率的缩写 PB是平均市净率的缩写 以下为百科知识 PE Price/Earnings 市盈率 也有叫做PER的,Price/Earnings Ratio 本益比,价格收益比,市盈率 市盈率 ...