【问题标题】:GATHER_PLAN_STATISTICS does does not generate basic plan statisticsGATHER_PLAN_STATISTICS 不生成基本计划统计信息
【发布时间】:2015-12-25 17:00:45
【问题描述】:

所有,

当我运行以下命令时,我现在正在学习调整查询:

select /*+ gather_plan_statistics */ * from emp;

select * from table(dbms_xplan.display(FORMAT=>'ALLSTATS LAST'));

结果总是说:

  • 警告:基本计划统计信息不可用。这些仅在以下情况下收集:
    • 提示“gather_plan_statistics”用于语句或
    • 参数“statistics_level”在会话或系统级别设置为“ALL”

我也在 sqlplus 中尝试了alter session set statistics_level = ALL;,但这并没有改变结果。

谁能告诉我我可能错过了什么?

非常感谢。

【问题讨论】:

  • 这里也一样。你解决问题了吗?

标签: sql oracle performance


【解决方案1】:

到目前为止我从答案中学到了什么:

在解析查询时,优化器会估计在查询计划的每个步骤中生成的行数。有时有必要检查预测的好坏。如果估计偏差超过一个数量级,则可能会导致使用错误的计划。

要比较估计数字和实际数字,需要执行以下步骤:

  1. 您需要对V$SQL_PLANV$SESSIONV$SQL_PLAN_STATISTICS_ALL 的读取权限。这些权限包含在SELECT_CATALOG 角色中。 (source)

  2. 通过

    开启统计数据收集

    ALTER SESSION SET STATISTICS_LEVEL = ALL;

    或在查询中使用提示/*+ gather_plan_statistics */

    似乎存在一定的性能开销。 例如,请参阅 Jonathan 的 blog

  3. 运行查询。您稍后需要找到它,因此最好包含任意提示:

    SELECT /*+ gather_plan_statistics HelloAgain */ * FROM scott.emp;

    EXPLAIN PLAN FOR SELECT ... 是不够的,因为它只会创建估计值而不运行实际查询。

    此外,正如@Matthew 所建议的(谢谢!),实际获取所有行非常重要。大多数 GUI 只会显示前 50 行左右。在 SQL Developer 中,您可以在查询结果窗口中使用快捷方式ctrl+End

  4. 在游标缓存中找到查询并注意它是SQL_ID

    SELECT sql_id, child_number, sql_text FROM V$SQL WHERE sql_text LIKE '%HelloAgain%';

    dbqbqxp9srftn 0 SELECT /*+ gather_plan...

  5. 格式化结果:

    SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('dbqbqxp9srftn',0,'ALLSTATS LAST'));

步骤 4. 和 5. 可以合并:

SELECT x.* 
  FROM v$sql s, 
       TABLE(DBMS_XPLAN.DISPLAY_CURSOR(s.sql_id, s.child_number)) x 
 WHERE s.sql_text LIKE '%HelloAgain%';

结果显示估计行(E-Rows)和实际行(A-Rows):

SQL_ID  dbqbqxp9srftn, child number 0
-------------------------------------
SELECT /*+ gather_plan_statistics HelloAgain */ * FROM scott.emp

Plan hash value: 3956160932

------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     14 |00:00:00.01 |       6 |
|   1 |  TABLE ACCESS FULL| EMP  |      1 |     14 |     14 |00:00:00.01 |       6 |
------------------------------------------------------------------------------------

【讨论】:

  • 不错的总结。我唯一要添加的是第 3 步。您需要运行查询获取所有结果。有时,使用 SQL*Navigator 之类的工具,开发人员会忘记在查看 DBMS_XPLAN 之前提取到末尾。
  • @MatthewMcPeak 感谢您的提示,这确实非常重要!
【解决方案2】:

DISPLAY Function 显示由EXPLAIN PLAN FOR 命令生成(填充)的 PLAN_TABLE 的内容。因此,您可以使用 EXPLAIN PLAN FOR 命令使用它来生成和显示(理论)计划,例如以这种方式:

create table emp as select * from all_objects;

explain plan for
select /*+ gather_plan_statistics */ count(*) from emp where object_id between 100 and 150;

select * from table(dbms_xplan.display );

Plan hash value: 2083865914

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     5 |   351   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|*  2 |   TABLE ACCESS FULL| EMP  |    12 |    60 |   351   (1)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJECT_ID"<=150 AND "OBJECT_ID">=100)

/*+gather_plan_statistics */hint 不会将数据保存到 PLAN_TABLE 中,但它会将执行统计信息存储在 V$SQL_PLAN 性能视图中。
要显示这些数据,您可以使用此处描述的方法:http://www.dba-oracle.com/t_gather_plan_statistics.htm,但这并不总是有效,因为您必须在 SQL 查询之后立即执行第二个命令。

比较好的方法是查询V$SQL视图获取查询的SQL_ID,然后使用DISPLAY_CURSOR函数,例如这样:

select /*+ gather_plan_statistics */ count(*) from emp where object_id between 100 and 150;

select sql_id, plan_hash_value, child_number, executions, fetches, cpu_time, elapsed_time, physical_read_requests, physical_read_bytes
from v$sql s
where sql_fulltext like 'select /*+ gather_plan_statistics */ count(*)%from emp%'
  and sql_fulltext not like '%from v$sql' ;

SQL_ID        PLAN_HASH_VALUE CHILD_NUMBER EXECUTIONS    FETCHES   CPU_TIME ELAPSED_TIME PHYSICAL_READ_REQUESTS PHYSICAL_READ_BYTES
------------- --------------- ------------ ---------- ---------- ---------- ------------ ---------------------- -------------------
9jjm288hx7buz      2083865914            0          1          1      15625        46984                     26            10305536

上面的查询返回SQL_ID=9jjm288hx7buzCHILD_NUMBER=0(子编号只是一个游标编号)。使用这些值来查询收集的计划:

SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('9jjm288hx7buz', 0, 'ALLSTATS'));

SQL_ID  9jjm288hx7buz, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from emp where object_id 
between 100 and 150

Plan hash value: 2083865914

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      2 |        |      2 |00:00:00.05 |   10080 |
|   1 |  SORT AGGREGATE    |      |      2 |      1 |      2 |00:00:00.05 |   10080 |
|*  2 |   TABLE ACCESS FULL| EMP  |      2 |     47 |     24 |00:00:00.05 |   10080 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(("OBJECT_ID"<=150 AND "OBJECT_ID">=100))

【讨论】:

  • 感谢您精心制作的答案,我学到了很多!
  • @wolφi 如果你喜欢这个答案,请upvote它,谢谢。
  • 很久以前就这样做了:-)
【解决方案3】:

如果您只运行问题中的两个语句:

select /*+ gather_plan_statistics */ * from emp;

select * from table(dbms_xplan.display(FORMAT=>'ALLSTATS LAST'));

那么我认为你的问题是你使用DBMS_XPLAN.DISPLAY。您使用它的方式是打印您解释的最后一条语句的计划,而不是您执行的最后一条语句。并且“解释”不会执行查询,因此它不会从gather_plan_statistics 提示中受益。

这在 12c 中适用于我:

select /*+ gather_plan_statistics */ count(*) from dba_objects;

SELECT *
FROM   TABLE (DBMS_XPLAN.display_cursor (null, null, 'ALLSTATS LAST'));

display_cursor 而不仅仅是display

【讨论】:

  • 当然——这很酷。不过,像 SQL*Developer 这样的现代 GUI 工具现在具有许多相同的功能。
  • 感谢您的回答我找到了以下解释stackoverflow.com/questions/9388837/…
  • 您认为我应该澄清我的答案还是将问题标记为重复?
  • 不,一点也不,请留下您的答案。我不知道赏金会发生什么......
【解决方案4】:

ALLSTATS LAST 在您运行该语句两次后开始工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-18
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 2021-12-23
    • 2018-11-06
    • 1970-01-01
    相关资源
    最近更新 更多