为什么预估运行计划与真实运行计划会有差异?

云和恩墨北区技术project师

专注于 SQL 审核和优化相关工作。以前服务的客户涉及金融保险、电信运营商、政府、生产制造等行业。

郭成日

本文由恩墨大讲堂154期线上分享整理而成。

一问题概要

对同一个 SQL 语句的 ExplainPlan 里显示的预估运行计划与通过 V$SQL_PLAN 视图获取的 Runtime Plan 真实运行计划,偶尔会发现两边有不一致的情况,为什么呢?为什么预估运行计划会不准确?如何才干避免这样的情况的发生?

二问题解答

这是运行计划相关中会被常常问道的问题,也是困扰自己非常长时间的问题。希望通过以下的分析能解释一部分原因。

对同一个 SQL 语句的 ExplainPlan 里显示的预估运行计划与通过 V$SQL_PLAN 视图获取的真实运行计划不一致的情况,其原因要比想象的很多其它种多样。

?

  • 绑定变量窥视(Bind Peeking):Explain Plan 里不会进行绑定变量窥视,可是 Runtime Plan 里会进行绑定变量窥视,所以,假设这样的情况发生会使两个运行计划会产生差异。
  • 隐式转换:Explain Plan?里不会考虑绑定变量的类型,可是?Runtime Plan?里会考虑类型,从而有可能会依据绑定变量的类型出现隐式转换,所以谓词(Predicate)会发生变化,使得运行计划也会产生差异。
  • 优化器參数:运行?Explain Plan?的?Session?与?Runtime Plan?的?Session?不是同一个。假设各个?Session?之间存在优化器參数差异,运行计划也会产生差异。
  • 统计信息收集參数:Explain Plan?始终是用最新的统计信息产生运行计划,可是,Runtime Plan?不一定会用最新的统计信息。因此也会产生运行计划差异。

?

预估运行计划与实际运行计划产生差异的原因总结为上面几种情况,当然也有因 Oracle Bug 的原因也会有产生运行计划的差异情况。

以下通过几个測试,加深对上面的问题的理解。

三測试环境

Oracle 版本号是 11.2.0.1的情况。

?

SQL> SELECT * FROMV$VERSION WHERE ROWNUM <= 1;

BANNER

--------------------------------------------------------------------------------

Oracle Database 11gEnterprise Edition Release 11.2.0.1.0 –Production

生成表 T1,T1 表有例如以下特点:


表名


列名


列类型


说明


T1


C1


Number


“1”值有10,000个,“1~10000”的值各一个,总共同拥有10,000种值


C2


Varchar2


同上

之后,对列 C1、C2 分别生成单列索引 IDX_T1_C1 和IDX_T1_C2。

?

SQL> CREATE TABLET1 ( C1 INT , C2 VARCHAR2(10));

?

表已创建。

?

SQL> INSERT INTOT1 SELECT 1, ‘1‘ FROM DUAL CONNECT BY LEVEL <= 10000;

?

已创建10000行。

?

SQL> INSERT INTOT1 SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 10000;

?

已创建10000行。

?

SQL> CREATE INDEXIDX_T1_C1 ON T1(C1);

?

索引已创建。

?

SQL> CREATE INDEXIDX_T1_C2 ON T1(C2);

?

索引已创建。

对表T1进行统计信息收集。

METHOD_OPT 的參数设为 ALLCOLUMNS SIZE 5 ,即。直方图的 BUCKETS 个数指定为5。可是列 C1 和 C2 有 10,000个不同的值。BUCKETS 个数为5的话。会生成等高直方图(HEIGHT BALANCED)。

SQL> EXECDBMS_STATS.gather_table_stats(user,‘T1‘, method_opt =>‘FOR ALL COLUMNS SIZE5‘);

?

PL/SQL 过程已成功完毕。

?

收集统计信息以后例如以下:

--table stats

SELECT t1.TABLE_NAME,

?????? t1.num_rows,

? ?????t1.SAMPLE_SIZE

FROM?? dba_tables t1

WHERE? table_name = ‘T1‘

AND??? t1.OWNER = user;

?

TABLE_NAME?? NUM_ROWS SAMPLE_SIZE

---------- ---------------------

T1????????????? 20000?????? 20000

?

--column stats

SELECT t2.TABLE_NAME,

?????? t2.COLUMN_NAME,

??????t2.NUM_DISTINCT,

?????? t2.NUM_NULLS,

?????? t2.DENSITY,

?????? t2.LOW_VALUE,

?????? t2.HIGH_VALUE,

?????? t2.HISTOGRAM

FROM?? dba_tab_columns t2

WHERE? t2.table_name = ‘T1‘

AND??? t2.OWNER = user‘;

?

TABLE COLUMNUM_DISTINCT NUM_NULLS DENSITY LOW_V HIGH_VALUE HISTOGRAM

----- ----------------- --------- ------- ----- ---------- --------------------

T1??? C1??????????10000???????? 0? 0.00005 C102?C302?????? HEIGHT BALANCED

T1??? C2??????????10000???????? 0? 0.00005 31???39393939?? HEIGHT BALANCED

?

--histogram stats

select t3.TABLE_NAME

?????? ,t3.COLUMN_NAME

?????? ,t3.ENDPOINT_NUMBER

?????? ,t3.ENDPOINT_VALUE

from? dba_tab_histograms t3

WHERE? t3.table_name = ‘T1‘

AND??? t3.OWNER = user;

?

TABLE COLUM ENDPOINTENDPOINT_VALUE

----- ----- ----------------------

T1??? C1??????????2????????????? 1

T1??? C1??????????3?????????? 2000

T1??? C1??????????4?????????? 6000

T1??? C1??????????5????????? 10000

T1??? C2??????????2 2.544225460682

T1??? C2??????????3 2.607349087913

T1??? C2??????????4 2.814229665870

T1??? C2??????????5 2.971215519298

四案列1:绑定变量窥视(Bind Peeking)

以下我们看下,因绑定变量窥视,而引起的预估运行计划与实际运行计划不一致的情况。首先。激活绑定变量窥视功能,默认值就是TRUE。

?

SQL> alter sessionset "_optim_peek_user_binds" = true;

?

会话已更改。

?

首先。我们输出预估运行计划。

从以下能够看到,运行计划选择的是索引范围扫描(Index Range Scan)的方式。

?

SQL> var b1number;

SQL> exec :b1 :=1;

?

PL/SQL 过程已成功完毕。

?

SQL> explain planfor

? 2?select count(c2)

? 3? fromt1

? 4?where c1 = :b1;

?

已解释。

?

SQL> select * fromtable(dbms_xplan.display(null,null,‘typical‘));

?

?

------------------------------------------------------------------------------------------

| Id? | Operation??????????????????? | Name????? | Rows?| Bytes | Cost (%CPU)| Time???? |

------------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT???????????? |?????????? |????1 |???? 6 |???? 2??(0)| 00:00:01 |

|?? 1 |?SORT AGGREGATE????????????? |?????????? |????1 |???? 6 |??????????? |????????? |

|?? 2 |??TABLE ACCESS BY INDEX ROWID| T1???????|???? 2 |??? 12 |????2?? (0)| 00:00:01 |

|*? 3 |???INDEX RANGE SCAN????????? |IDX_T1_C1 |???? 2 |?????? |????1?? (0)| 00:00:01 |

------------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 3 - access("C1"=TO_NUMBER(:B1))

?

绑定变量B1的实际值是“1”。

T1表里值为1的记录数将近占50%,这样的情况与其选择索引范围扫,不如选择全表扫(Table Full Scan)会有效率一些。可是 ExplainPlan命令不进行绑定变量的窥视,即,在创建预估运行计划的过程中,会把绑定变量的值设为未知(Uknown)来处理,不会考虑实际的绑定变量的值究竟是什么。所以,ExplainPlan 不关心其值是不是“1”,而仅仅考虑 Distinct Count 来建立运行计划。

等高直方图(HEIGHT BALANCED)存在的时候,预估行数会通过 DistinctCount 列进行计算。计算公式例如以下:

?

预估行数 = 所有行数 / Distinct Count = 20,000 /10,000? =?2?

???

可是。实际运行计划与上面的结果全然不一样,例如以下。

?

SQL> select /*+gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c1 = :b1;

?

?COUNT(C2)

----------

???? 10001

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘typical‘));

?

---------------------------------------------------------------------------

| Id? | Operation????????? | Name | Rows? | Bytes | Cost (%CPU)| Time???? |

---------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT?? |?????|?????? |?????? |???12 (100)|????????? |

|?? 1 |?SORT AGGREGATE??? |????? |????1 |???? 6 |??????????? |????????? |

|*? 2 |??TABLE ACCESS FULL| T1?? |? 8000 | 48000 |??? 12??(0)| 00:00:01 |

---------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - filter("C1"=:B1)

能够看到。使用了绑定变量窥视,即,优化器在创建运行计划前读取了绑定变量的实际的值(进行是窥视)。之后。參考绑定变量的值来创建运行计划。

这个样例。使用了值“1”来创建了运行计划。

所以。预估行数从 ExplainPlan 里的2 变成了8000。其原因例如以下:

预估行数 = ?值“1”的 buckets 数 * buckets 的高度

? ? ? ? ? ? ? ?=?2 *(20000 / 5 ) =?8000

实际行数为10,001,预估值与实际值相当接近了。

使用 DBMS_XPLAN.DISPLAY_CURSOR函数的时候,參数里假设加上 +PEEKED_BBINDS 的话,运行计划里能够看到绑定变量窥视的值。

?

SQL> select /*+gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c1 = :b1;

?

?COUNT(C2)

----------

???? 10001

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘all +peeked_binds‘));

?

?

---------------------------------------------------------------------------

| Id? | Operation????????? | Name | Rows? | Bytes | Cost (%CPU)| Time???? |

---------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT?? |?????|?????? |?????? |???12 (100)|???????? ?|

|?? 1 |?SORT AGGREGATE??? |????? |????1 |???? 6 |??????????? |????????? |

|*? 2 |??TABLE ACCESS FULL| T1?? |? 8000 | 48000 |??? 12??(0)| 00:00:01 |

---------------------------------------------------------------------------

?

Query Block Name /Object Alias (identified by operation id):

-------------------------------------------------------------

?

?? 1 - SEL$1

?? 2 - SEL$1 / [email protected]$1

?

Peeked Binds(identified by position):

--------------------------------------

?

?? 1 - :B1 (NUMBER): 1

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - filter("C1"=:B1)

?

Column ProjectionInformation (identified by operation id):

-----------------------------------------------------------

?

?? 1 - (#keys=0) COUNT("C2")[22]

?? 2 - "C2"[VARCHAR2,10]

?

绑定变量窥视(Binding Peeking)与绑定变量捕获(Bind Capture)常常弄混。绑定变量捕获(Bind Capture)是对特定 SQL 里使用的绑定变量值依照固定周期放到 SGA 里保存的情况。最初的绑定变量窥视与绑定变量捕获的时间是一样。约15分钟(900秒)后,绑定变量捕获会再次发生,周期性重复发生。以下能够查看绑定变量捕获的信息。

?

SELECT t4.NAME,

?????? t4.POSITION,

??????t4.VALUE_STRING,

?????? t4.WAS_CAPTURED,

?????? t4.LAST_CAPTURED

FROM?? V$sql_bind_capture t4

WHERE? sql_id = ‘bqqp887001jj8‘;

?

NAME? POSITION VALUE WAS_C LAST_CAPTU

----- -------- ---------- ----------

:B1????????? 1 1???? YES??11-4月 -18

五案列2:绑定变量类型问题

首先,为了证明这个測试不是由于上面的绑定变量窥视而引起的不一致。所以把绑定变量窥视功能关掉了。

SQL> alter sessionset "_optim_peek_user_binds" = false;

?

会话已更改。

?

对 C2 列使用绑定变量,进行观察。从以下能够看到,预估运行计划里使用了索引。

?

SQL> var b2number;

SQL> exec :b2 :=1;

?

PL/SQL 过程已成功完毕。

?

SQL> explain planfor

? 2? selectcount(c2)

? 3? fromt1

? 4?where c2 = :b2;

?

已解释。

?

SQL> select * fromtable(dbms_xplan.display(null,null,‘typical‘));

?

-------------------------------------------------------------------------------

| Id? | Operation???????? | Name????? | Rows?| Bytes | Cost (%CPU)| Time???? |

-------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT? |??????????|???? 1 |???? 4 |????1?? (0)| 00:00:01 |

|?? 1 |?SORT AGGREGATE?? |?????????? |????1 |???? 4 |??????????? |?????????|

|*? 2 |??INDEX RANGE SCAN| IDX_T1_C2 |????2 |???? 8 |???? 1??(0)| 00:00:01 |

-------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - access("C2"=:B2)

?

ExplainPlan 命令仅仅会查看是否存在绑定变量。而不会考虑绑定变量的类型是什么,其值是什么,始终会把绑定变量的类型设为 VARCHAR2 类型进行考虑。所以,上面的样例里无论对绑定变量B2如何定义。ExplainPlan 里预估运行计划始终是一样。

可是,真实运行计划里没有选择 INDEX RANGE SCAN,而是选择了 TABLE FULL SCAN。

?

SQL> select /*+gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c2 = :b2;

?

?COUNT(C2)

----------

???? 10001

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘allstats last‘));

?

?

-------------------------------------------------------------------------------------

| Id? | Operation????????? | Name | Starts | E-Rows | A-Rows|?? A-Time?? | Buffers |

-------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT?? |?????|????? 1 |??????? |?????1 |00:00:00.01 |????? 39 |

|?? 1 |?SORT AGGREGATE??? |????? |?????1 |????? 1 |????? 1 |00:00:00.01 |????? 39 |

|*? 2 |??TABLE ACCESS FULL| T1?? |????? 1 |?????2 |? 10001 |00:00:00.01 |????? 39 |

-------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - filter(TO_NUMBER("C2")=:B2)

?

为什么会发生这样的情况?绑定变量窥视功能已经关闭了。所以肯定不是绑定变量窥视的问题。

这里须要注意的是,C2 列是 VARCHAR2 类型。绑定变量 B2 是 NUMBER类型。这时,Oracle 会进行隐式转换。VARCHAR2 类型会被转换成 NUMBER 类型,即。NUMBER 类型的优先级更高。所以。会对C2列进行隐式转换(VARCHAR2 →NUMBER),从而不能使用C2列的索引。

能够在谓词信息(Predicate Information)中确认。

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - filter(TO_NUMBER("C2")=:B2)

?

为了再次证明这个是由于隐式转换的问题。我们使用 VARCHAR2 类型的绑定变量 B3 进行測试。

?

SQL> var b3varchar2(10);

SQL> exec :b3 :=‘1‘;

?

PL/SQL 过程已成功完毕。

?

SQL> select /*+gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c2 = :b3;

?

?COUNT(C2)

----------

???? 10001

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘allstats last‘));

?

?

-----------------------------------------------------------------------------------------

| Id? | Operation???????? | Name????? | Starts | E-Rows | A-Rows |?? A-Time??| Buffers |

-----------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT? |??????????|????? 1 |??????? |?????1 |00:00:00.01 |????? 20 |

|?? 1 |?SORT AGGREGATE?? |?????????? |????? 1 |?????1 |????? 1 |00:00:00.01 |????? 20 |

|*? 2 |??INDEX RANGE SCAN| IDX_T1_C2 |?????1 |????? 2 |? 10001 |00:00:00.01 |????? 20 |

-----------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - access("C2"=:B3)

?

从上面能够看到,绑定变量类型是 VARCHAR2 的时候,没有进行隐式转换,产生了与预估运行计划同样的运行计划,使用了索引的范围扫描。

这个样例也说明,不能全然相信预估的运行计划。内部的一些转换(比方列的隐式转换)会使运行计划改变,甚至有时候会出现不希望的运行计划。

六案列3:统计信息收集的參数问题

以下 SQL 的预估运行计划与实际运行计划全然一致。

?

?SQL> explain planfor

? 2?select count(c2)

? 3? fromt1

? 4?where c1 = 2;

?

已解释。

?

------------------------------------------------------------------------------------------

| Id? | Operation??????????????????? | Name????? | Rows?| Bytes | Cost (%CPU)| Time???? |

------------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT???????????? |?????????? |????1 |???? 6 |???? 2??(0)| 00:00:01 |

|?? 1 |?SORT AGGREGATE????????????? |?????????? |????1 |???? 6 |??????????? |????????? |

|?? 2 |??TABLE ACCESS BY INDEX ROWID| T1???????|???? 1 |???? 6 |????2?? (0)| 00:00:01 |

|*? 3 |???INDEX RANGE SCAN????????? |IDX_T1_C1 |???? 1 |?????? |????1?? (0)| 00:00:01 |

------------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 3 - access("C1"=2)

?

?

SQL> select? /*+ gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c1 = 2;

?

?COUNT(C2)

----------

???????? 1

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘allstats last‘));

?

----------------------------------------------------------------------------------------------------

| Id? | Operation??????????????????? | Name????? | Starts | E-Rows | A-Rows |?? A-Time??| Buffers |

----------------------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT???????????? |?????????? |????? 1 |???????|????? 1 |00:00:00.01 |?????? 3 |

|?? 1 |?SORT AGGREGATE????????????? |?????????? |????? 1 |?????1 |????? 1 |00:00:00.01 |?????? 3 |

|?? 2 |??TABLE ACCESS BY INDEX ROWID| T1???????|????? 1 |????? 1 |?????1 |00:00:00.01 |?????? 3 |

|*? 3 |???INDEX RANGE SCAN????????? |IDX_T1_C1 |????? 1 |????? 1 |?????1 |00:00:00.01 |?????? 2 |

----------------------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 3 - access("C1"=2)

?

对 T1 表的 C1 = 2。C2=1 的值添加20,000个,之后又一次收集统计信息,可是 NO_INVALIDATE 參数设为 NULL,NULL 的意思是让 Oracle 自己主动处理的意思。

NO_INVALIDATE 其它參数情况參考例如以下:

?

  • NO_INVALIDATE=TRUE:更新统计信息。但对有从属(Dependency)关系的 SQL 不进行Invalidation。为了避免一次性大量的硬解析(Hard Parse)现象的发生。SQL 假设在 SGA 里 Age Out 后,再次运行的时候,才会用到更新后的统计信息。
  • NO_INVALIDATE=FALSE:更新统计信息,并对有从属(Dependency)关系的 SQL 立即进行 Invalidation。
  • NO_INVALIDATE=AUTO(NULL):更新统计信息,但对有从属关系的 SQL 不会一次性的进行 Invalidation,而是在最大5小时(18,000秒)内随机进行 Invalidation 的方式进行。能够说是 TRUE 与 FALSE 的中间形式。18,000秒是能够通过 _OPTIMIZER_INVALIDATION_PERIOD 參数进行设定。

?

如今对表T1添加数据。并收集统计信息,可是 NO_INVALIDATE 參数设为 NULL(默认值是 NULL)。

?

?SQL> insert intot1 select 2,‘1‘ from dual connect by level <= 20000;

?

已创建20000行。

?

SQL> execdbms_stats.gather_table_stats(user,‘T1‘,method_opt => ‘for all columns size5‘,no_invalidate => null);

?

PL/SQL 过程已成功完毕。

?

Explain Plan 命令始终是在用最新的统计信息,所以从以下能够看到,ExplainPlan 命令对 C1=2 的条件使用了最新的统计信息,运行计划选择了 Table Full Scan。

预估行数为 16,000 行,与实际行数 20,001 行数相当接近。由于存在列的直方图。这样的预估是可行的。

?

?SQL> explain planfor

? 2?select count(t1.c2)

? 3? fromsys.t1

? 4?where t1.c1 = 2 ;

?

已解释。

?

SQL> select * fromtable(dbms_xplan.display());

?

?

---------------------------------------------------------------------------

| Id? | Operation????????? | Name | Rows? | Bytes | Cost (%CPU)| Time???? |

---------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT?? |?????|???? 1 |???? 6 |???20?? (0)| 00:00:01 |

|?? 1 |?SORT AGGREGATE??? |????? |????1 |???? 6 |??????????? |????????? |

|*? 2 |??TABLE ACCESS FULL| T1?? | 16000 |96000 |??? 20?? (0)| 00:00:01 |

---------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 2 - filter("T1"."C1"=2)

?

可是。在真实运行计划中仍然选择了 Index Range Scan。由于尽管统计信息更新了。可是相关的 SQL 还没有被 Invalidation。

?

?SQL> select? /*+ gather_plan_statistics */ count(c2)

? 2? fromt1

? 3?where c1 = 2;

?

?COUNT(C2)

----------

???? 20001

?

SQL> select * fromtable(dbms_xplan.display_cursor(null,null,‘allstats last‘));

?

?

----------------------------------------------------------------------------------------------------

| Id? | Operation??????????????????? ??| Name?????| Starts | E-Rows | A-Rows |??A-Time?? | Buffers |

----------------------------------------------------------------------------------------------------

|?? 0 | SELECT STATEMENT???????????? ??|???? ??????|?????1 |??????? |????? 1 |00:00:00.02 |???? 102 |

|?? 1 |?SORT AGGREGATE????????????? ??|??????????|????? 1 |????? 1 |?????1 |00:00:00.02 |???? 102 |

|?? 2 |??TABLE ACCESS BY INDEX ROWID | T1???????|????? 1 |????? 2 |?20001 |00:00:00.02 |??? ?102 |

|*? 3 |???INDEX RANGE SCAN????????? ??| IDX_T1_C1 |????? 1 |?????2 |? 20001 |00:00:00.01 |????? 70 |

----------------------------------------------------------------------------------------------------

?

Predicate Information(identified by operation id):

---------------------------------------------------

?

?? 3 - access("C1"=2)

?

假设仅仅看 Explain Plan 后就推断“运行计划的效率不错”是不可取的。会依据不同的情况产生非常大的性能差异。这时能够通过 DBMS_SHARED_POOL.PURGE 存储过程,或使用 清理共享池(Shared Pool Flush)等方法强制反应最新的统计信息。

七总结

预估运行计划与真实运行计划产生差异的原因。事实上是多种多样的,在分析其原因的过程中发现须要相当多的知识点。

产生差异的原因,当中最普遍的有因绑定变量的窥视。也有因绑定变量的隐式转换。也有因參数差异,也有因统计信息收集參数等问题。

不能对预估运行计划100%信任,一定要实际运行以后验证其结果。假设这个过程中想解释运行计划异常的现象。须要了解 DBMS_XPLAN 包的用法与对其结果的正确理解。

资源下载

关注公众号:数据和云(OraNews)回复keyword获取

‘2017DTC’。2017?DTC?大会?PPT

‘DBALIFE’,“DBA?的一天”海报

‘DBA04’,DBA?手记4?经典篇章电子书

‘RACV1’,?RAC?系列课程视频及?PPT

‘122ARCH’。Oracle?12.2?体系结构图

‘2017OOW’,Oracle?OpenWorld?资料

‘PRELECTION’,大讲堂讲师课程资料

wx_fmt=png" alt="640?wx_fmt=png" />

原文地址:https://www.cnblogs.com/ljbguanli/p/9898248.html

时间: 2024-10-25 09:13:35

为什么预估运行计划与真实运行计划会有差异?的相关文章

Oracle 查询真实执行计划

什么是真实执行计划 获取Oracle的执行计划,有几种方式.(本文使用Oracle 11g XE版本,以及普通用户scott登录) explain plan for 有两个步骤: explain plan for ${SQL} select * from table(dbms_xplan.display); 这一个方法可以在PLSQLDev的cmd窗口和sql窗口执行,同时不需要给用户授权. 示例: autotrace 有两个步骤: set autot on 执行${SQL} 但普通用户需要授权

Windows Server 2008 任务计划无法自动运行的解决办法

问题:编写的bat脚本,直接执行,成功:但是在任务管理器中配置该任务,运行不成功,结果显示为:0x1,系统环境为 Windows Server 2008. 分析:bat任务没有调用执行. 解决方案: 尝试换用户创建任务也不行,最后网上的一篇文章提供了线索. 在任务启动的“操作”窗口,除了原本的启动程序路径,添加属性“起始于”(将bat文件的上层目录填入) 附:http://www.cnblogs.com/mannyzhoug/archive/2013/05/28/3104030.html Win

【MM 计划协议】采购计划协议Schedule Agreement

一.计划协议的前台操作 1)创建计划协议 T-code:ME31L 填入相应字段后,进入行行项目细节数据界面: 填好后,点击保存. 2)手工创建计划行 T-code:ME38 点击回车: 选中需要创建计划行的项目,点击按钮"交货计划",进入交货计划界面: 将字段"类"."交货日期"."已计划数量"字段填写好,点击回车,若系统没有报错,点击保存. 3)收货 T-code:MIGO(在这里不在详细描述,只是需要注意收货日期要等于或

用任务计划管理计划任务对付任务计划-禁止WPS提示升级

作为一名至今还在坚守着64位XP的XP用到死星人,因为准备升级电脑,所以准备移民外星,开始使用Windows7.其实我新电脑买来一年了,为了坚守XP,扔在一边没有装(华硕Z9PE-D8 WS主板,双E5-2637CPU,32G内存,8个SSD硬盘,4个显卡). 为了装新机器,同时为了拾起扔了不少年的电脑技术,研究了一段时间的Windows7,也看了一下Windows8和10,真没有多少让人欢喜的功能.能值得称道的就是Windows8的任务管理器了,确实很不错,很漂亮也很人性化. 其他UAC.库等

【计划】六月自学计划

又是一年高考时.为遇见和自己一样的人,为成全你想要的自己.当我读到这句话,我就很喜欢叻. 工作是自己一辈子的事情,实习是工作的前奏.当然要认真对待,谨慎选择.选择自己喜欢的,这辈子才不会活得厌烦. 我找到不错的资料,这个月是考试月,复习与自学并行,希望可以快速进步,干巴爹. [计划]六月自学计划,布布扣,bubuko.com

深入解析父子线程(父子线程相互独立,子线程使用自己的栈空间,进程要等到所有线程终止运行之后才终止运行)

说起多线程,我们可以说起一大堆和它相关的有趣话题来,比如什么子孙线程关系,父子线程关系,线程同步异步之类的研究话题来,而我今天所说的,是关于父子线程的一些有趣现象. 首先提出一个问题,“在多线程的应用程序中,当父线程结束之后,子线程会不会退出呢?”,本文将围绕这个问题,深入分析windows中父子线程的生命周期及他们之间的关系. 我们知道,不管你使用的是何种编程语言,但当我们在windows下编程,且使用了平台相关性的库的时候,不管我们使用什么函数来创建线程,最终的结果是,我们的代码中都会调用C

在shell中运行以不同方式运行脚本

在shell当中,可以有3中方式运行脚本: 1 . ./script_name 或者source ./script_name 2 直接./script_name 3 ./script_name & 加入脚本script_name中有两个命令command1,command2,那么,当以第一种方式运行时,command1和command2都在当前shell中运行,并没有产生subshell,如下图所示: 当script_name以第2中方式运行时,当前shell会产生一个subshell,comm

Excel 如何在程序运行期间提示信息“正在运行中。。。请稍候

Excel 如何在程序运行期间提示信息“正在运行中...请稍候 : 显示窗体的语句(窗体有2种模式) (1)模式窗体的显示代码: 窗体名称.Show  vbModal 也可以写成:窗体名称.Show 1 (2)无模式窗体的显示代码: 窗体名称.Show  vbModeless 也可以写成:窗体名称.Show 0 (1)模式窗体:窗体显示后将停显示之后的代码直到退出或隐藏此窗体,并且必须退出或隐藏此窗体后,才可以操作非此窗体的其他界面元素. 也就是说,当你使用模式窗体显示某窗体的时候,当程序执行完

软考知识点梳理--项目进度计划与项目进度管理计划

项目进度计划 项目进度计划是进度模型的输出,展示活动之间的相互关联,以及计划日期.持续时间.里程碑和所需资源.项目进度计划中至少要包括每个活动的计划开始日期与计划结束日期.即使在早期阶段就进行了资源规划,在未确认资源分配和计划开始与结束日期之前,项目进度计划都只是初步的.一般要在项目管理计划编制完成之前进行这些确认.还可以编制一份目标项目进度模型,规定每个活动的目标开始日期与目标结束日期. 项目进度计划是利用各种进度计划编织工具,如关键路径法.关键链法.资源平衡.进度压缩及相关项目管理软件等,在