ITPub博客

首页 > 数据库 > Oracle > [20241216]Oracle wrong result一则(优化器问题)2.txt

[20241216]Oracle wrong result一则(优化器问题)2.txt

原创 Oracle 作者:lfree 时间:2024-12-16 21:44:54 0 删除 编辑

[20241216]Oracle wrong result一则(优化器问题)2.txt

--//链接提到的问题https://www.cnblogs.com/PiscesCanon/p/12723513.html,自己测试看看。

1.环境:
SCOTT@book> @ver1
PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
--//注:在21c测试不出来问题。

2.问题提出:
SCOTT@book> create table test as select * from dba_objects;
Table created.
--//分析略。

SCOTT@book> with t as (select a.*,rownum rn from dba_objects a) select count(*) from t where t.rn=(select max(rn) from t);
  COUNT(*)
----------
         1

SCOTT@book> with t as (select a.*,rownum rn from test a) select count(*) from t where t.rn=(select max(rn) from t);
  COUNT(*)
----------
         0
--//理论讲至少存在1行。为什么呢?如果对比执行计划可以发现前者使用类似MATERIALIZE的提示,执行计划出现 TEMP TABLE TRANSFORMATION。
--//而后者没有出现。加入提示/MATERIALIZE也可以纠正这个错误。

COTT@book> with t as (select /*+ MATERIALIZE */ a.*,rownum rn from test a) select count(*) from t where t.rn=(select max(rn) from t);
  COUNT(*)
----------
         1

--//既然真实的表执行测试出现问题,可以直接采用真实表测试。

SCOTT@book> create table ta as select  rownum id  from dual ;
Table created.
--//分析表略。

SCOTT@book> with t as (select a.*,rownum rn from ta a) select * from t where t.rn=(select max(rn) from t);
        ID         RN
---------- ----------
         1          1
--//可以发现当表有1条记录时,执行结果是正确的。

SCOTT@book> insert into ta values(2);
1 row created.

SCOTT@book> commit;
Commit complete.
--//插入1条记录,分析表略。

SCOTT@book> with t as (select a.*,rownum rn from ta a) select * from t where t.rn=(select max(rn) from t);
no rows selected

--//可以发现当表有2条记录时,执行结果是错误的,为什么呢?

3.分析:
--//查看执行计划:
SCOTT@book> @ dpc '' outline ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  fytayvxpfzz5w, child number 0
-------------------------------------
with t as (select a.*,rownum rn from ta a) select * from t where
t.rn=(select max(rn) from t)
Plan hash value: 187064592
-------------------------------------------------------------------------------
| Id  | Operation             | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |        |       |     8 (100)|          |
|*  1 |  VIEW                 |      |      2 |    52 |     4   (0)| 00:00:01 |
|   2 |   COUNT               |      |        |       |            |          |
|   3 |    TABLE ACCESS FULL  | TA   |      2 |     6 |     4   (0)| 00:00:01 |
|   4 |   SORT AGGREGATE      |      |      1 |    13 |            |          |
|   5 |    VIEW               |      |      2 |    26 |     4   (0)| 00:00:01 |
|   6 |     COUNT             |      |        |       |            |          |
|   7 |      TABLE ACCESS FULL| TA   |      2 |       |     4   (0)| 00:00:01 |
-------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$EA29B7B6 / T@SEL$2
   2 - SEL$EA29B7B6
   3 - SEL$EA29B7B6 / A@SEL$EA29B7B6
   4 - SEL$3
   5 - SEL$1        / T@SEL$3
   6 - SEL$1
   7 - SEL$1        / A@SEL$1

Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
      DB_VERSION('11.2.0.4')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      OUTLINE_LEAF(@"SEL$3")
      OUTLINE_LEAF(@"SEL$EA29B7B6")
      OUTLINE_LEAF(@"SEL$2")
      NO_ACCESS(@"SEL$2" "T"@"SEL$2")
      PUSH_SUBQ(@"SEL$3")
      FULL(@"SEL$EA29B7B6" "A"@"SEL$EA29B7B6")
      NO_ACCESS(@"SEL$3" "T"@"SEL$3")
      FULL(@"SEL$1" "A"@"SEL$1")
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("T"."RN"=)

--//我开始也怀疑Predicate Information 看到的过滤条件,没有信息,不过使用explain plan看有信息。

SCOTT@book> with t as (select a.*,rownum rn from ta a) select * from t where t.rn=(select max(rn) from t);
no rows selected

SCOTT@book> @ hash

HASH_VALUE SQL_ID        CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX   SQL_EXEC_START      SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
1794112700 fytayvxpfzz5w            0     130236       187064592  6aeffcbc  2024-12-16 09:38:53    16777220

SCOTT@book> @ expand_sql_text11g.sql fytayvxpfzz5w
PL/SQL procedure successfully completed.
M_SQL_OUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT "A1"."ID" "ID","A1"."RN" "RN" FROM  (SELECT "A4"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A4") "A1" WHERE "A1"."RN"= (SELECT MAX("A2"."RN") "MAX(RN)" FRO
M  (SELECT "A3"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A3") "A2")

--//格式化如下:
SELECT COUNT (*) "COUNT(*)"
  FROM (SELECT "A4"."ID" "ID"
              ,ROWNUM "RN"
          FROM "SCOTT"."TA" "A4") "A1"
 WHERE "A1"."RN" = (SELECT MAX ("A2"."RN") "MAX(RN)"
                      FROM (SELECT "A3"."ID" "ID"
                                  ,ROWNUM "RN"
                              FROM "SCOTT"."TA" "A3") "A2");

COUNT(*)
----------
         1
 
--//当我使用展开的sql语句执行结果又是正确的。

SELECT *
  FROM (SELECT "A4"."ID" "ID"
              ,ROWNUM "RN"
          FROM "SCOTT"."TA" "A4") "A1"
 WHERE "A1"."RN" = (SELECT MAX ("A2"."RN") "MAX(RN)"
                      FROM (SELECT "A3"."ID" "ID"
                                  ,ROWNUM "RN"
                              FROM "SCOTT"."TA" "A3") "A2");

--//仔细看前面的执行计划id=2,6存在两次count,因为没有使用MATERIALIZE提示,结果集不确定(也就是rownum对应的rn在变)。
--//如何验证我的推测呢?执行如下sql语句:

SCOTT@book> with t as (select a.*,rownum rn from ta a) select * from t where t.id=(select count(*) from t);
        ID         RN
---------- ----------
         2          3

--//注意看rn=3.不是2.(感觉应该是4),不过至少说明select * from t中rn还在变化,只能讲11g这个版本存在bug。
--//这样就很容易理解,为什么表仅仅1条记录时,有显示输出,第2次计数从1开始,这样有输出。
--//注意看前面rn=3,第2次计数从2开始,我的理解。

SCOTT@book> @ hash
HASH_VALUE SQL_ID        CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX   SQL_EXEC_START      SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
2975981936 5bv0gm6sq3sbh            0     123248       187064592  b161e170  2024-12-16 09:45:19    16777217

SCOTT@book> @ expand_sql_text11g.sql 5bv0gm6sq3sbh
PL/SQL procedure successfully completed.

M_SQL_OUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT "A1"."ID" "ID","A1"."RN" "RN" FROM  (SELECT "A4"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A4") "A1" WHERE "A1"."ID"= (SELECT COUNT(*) "COUNT(*)" FROM  (S
ELECT "A3"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A3") "A2")

SCOTT@book> SELECT "A1"."ID" "ID","A1"."RN" "RN" FROM  (SELECT "A4"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A4") "A1" WHERE "A1"."ID"= (SELECT COUNT(*) "COUNT(*)" FROM  (SELECT "A3"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A3") "A2");
        ID         RN
---------- ----------
         2          2
--//展开执行是正确的。

4.看看 dbms_sqldiag的诊断分析:

SCOTT@book> @ 10053x 5bv0gm6sq3sbh 0
PL/SQL procedure successfully completed.

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "T"."ID" "ID","T"."RN" "RN"
  FROM (SELECT "A"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A") "T"
 WHERE "T"."ID" = (SELECT COUNT(*) "COUNT(*)"
  FROM (SELECT ROWNUM "RN" FROM "SCOTT"."TA" "A") "T")

SCOTT@book> with t as (select a.*,rownum rn from ta a) select * from t where t.rn=(select max(rn) from t);
no rows selected

SCOTT@book> @ hash
HASH_VALUE SQL_ID        CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX   SQL_EXEC_START      SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
1794112700 fytayvxpfzz5w            0     130236       187064592  6aeffcbc  2024-12-16 09:52:12    16777221

SCOTT@book> @ 10053x fytayvxpfzz5w 0
PL/SQL procedure successfully completed.

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "T"."ID" "ID","T"."RN" "RN"
  FROM (SELECT "A"."ID" "ID",ROWNUM "RN" FROM "SCOTT"."TA" "A") "T"
 WHERE "T"."RN" = (SELECT MAX("T"."RN") "MAX(RN)"
  FROM (SELECT ROWNUM "RN" FROM "SCOTT"."TA" "A") "T")
--//按照展开执行结果是正确的。这样看来11g并不是按照展开的方式执行。

5.附上测试使用相关sql脚本:

$ cat 10053x.sql
execute dbms_sqldiag.dump_trace(p_sql_id=>'&1',p_child_number=>&2,p_component=>'Compiler',p_file_id=>'a'||'&&1');

$ cat expand_sql_text11g.sql
variable m_sql_out clob

declare
    m_sql_in  clob ;
begin
        select sql_fulltext into m_sql_in from v$sqlarea where sql_id='&&1';
        dbms_sql2.expand_sql_text(        -- 11g
                m_sql_in,
                :m_sql_out
        );
--      if version = '12' then
--        select sql_fulltext into m_sql_in from v$sqlarea where sql_id='&&1';
--        dbms_utility.expand_sql_text(     -- 12c
--                m_sql_in,
--                :m_sql_out
--        );
--      end if;
end;
/
set long 20000
column m_sql_out format a160
print m_sql_out


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/267265/viewspace-3056973/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    4006
  • 访问量
    7969295