Oracle 12C Statistics on Column Groups

Statistics on Column Groups
单个列统计信息对于判断where子句中的单个谓词的选择性是非常有用的。然而,当where子句中包含来自相同表的不同列的多个谓词时,单个列统计信息不能显示列之间的关系。使用列组(column group)就是用来解决这个问题的。优化器单独计算谓词的选择性,然后合并它们。然而,如果在单列之间存在关联,那么优化器当评估基数时不会考虑它,优化器会使用每个表谓词的选择性来乘以行数来评估基数。

下面的语句查询dba_tab_col_statistics表来显示关于sh.customers表中列cust_state_province与country_id列的统计信息。

SQL> COL COLUMN_NAME FORMAT a20
SQL> COL NDV FORMAT 999
SQL> SELECT COLUMN_NAME, NUM_DISTINCT AS "NDV", HISTOGRAM
  2  FROM DBA_TAB_COL_STATISTICS
  3  WHERE OWNER = 'SH'
  4  AND TABLE_NAME = 'CUSTOMERS'
  5  AND COLUMN_NAME IN ('CUST_STATE_PROVINCE', 'COUNTRY_ID');

COLUMN_NAME           NDV HISTOGRAM
-------------------- ---- ---------------
CUST_STATE_PROVINCE   145 FREQUENCY
COUNTRY_ID             19 FREQUENCY

下面的语句查询住在California的客户人数3341人:

SQL> SELECT COUNT(*)
  2  FROM sh.customers
  3  WHERE cust_state_province = 'CA';

  COUNT(*)
----------
      3341

来显示查询state为CA,country_id为52790(USA)的客户人数的查询执行

SQL> EXPLAIN PLAN FOR
  2  SELECT *
  3  FROM sh.customers
  4  WHERE cust_state_province = 'CA'
  5  AND country_id=52790;

Explained.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2008213504

-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |  1115 |   205K|   423   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| CUSTOMERS |  1115 |   205K|   423   (1)| 00:00:01 |
-------------------------------------------------------------------------------

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("CUST_STATE_PROVINCE"='CA' AND "COUNTRY_ID"=52790)

Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel thre
shold


17 rows selected.

基于单列country_id和cust_state_province列的统计信息,优化器评估住在USA的California的客户人数是1115,而实际上有3341人,但优化器不知道,所以通过所有谓词减少了返回的行数因此大大降低了评估基数。可以通过收集列组统计信息来让优化器知晓列country_id与cust_state_province之间的真实关系。

自动与手动列组统计信息
Oracle数据库可以自动或手动创建列组统计信息。优化器可以使用SQL执行计划指令来生成更优的执行计划。如果dbms_stats引用参数auto_stat_extensions被设置为ON(缺省值为OFF),那么SQL执行计划指令基于工作量中谓词的使用情况可以自动触发来创建列组统计信息。可以通过set_table_prefs,set_global_prefs或set_schema_prefs过程来设置auto_stat_extensions。

当想要手动管理列组统计信息时,可以使用dbms_stats来执行以下操作:
.探测列组
.创建以前探测到的列组
.手动创建列组并收集列组统计信息

列组统计信息用户接口
有几个dbms_stats程序单元有与列组相关的引用参数
seed_col_usage过程,迭代指定工作量中的SQL语句,编译它们,然后查看在这些语句谓词中出现列的使用信息。为了决定合适的列组,数据库必须观察一个有代表性的工作量。在监控期间不需要运行查询本身。可以对在工作量中那些运行时间长的查询执行explain plan来确保数据库记录这些查询所使用的列组信息。

report_col_usage函数,生成一个报告列出在工作量中所看到的过滤谓词,连接谓词与group by子句中的列。可以使用这个函数来检查对于指定表所记录的列使用信息。

create_extended_stats函数,创建扩展,它可以是列组或表达式。当用户手动或自动统计信息收集任务对表收集统计信息时数据库会对扩展收集统计信息。

auto_stat_extensions引用参数,控制自动创建扩展,包括列组,当优化器统计信息被收集时,使用set_table_prefs,set_schema_prefs或set_global_prefs来设置这个引用参数。当auto_stat_extensions被设置为off(缺省值)时,数据库不会自动创建列组统计信息。为了创建扩展,你必须执行create_extended_stats函数或在dbms_stats API中的method_opt参数中显性指定扩展统计信息。当auto_stat_extensions设置为ON时,一个SQL执行计划指令基于工作量中谓词中列的使用信息可以触发自动创建列组统计信息。

为特定的工作量检测有用的列组
可以使用dbms_stats.seed_col_usage与report_col_usage来基于特定工作量来决定那个表需要列组。当你不知道需要创建什么样的扩展统计信息时这种技术很有用。这种技术对于扩展统计信息不会工作。

假设存在以下情况:
.查询sh.customers_test表(用customers表来创建)并在谓词中使用了country_id与cust_state_province列但基数评估不正确。

.想要数据库监控工作量5分钟(300秒)。

.想要数据库自动判断需要那些列组。

为了检测列组需要执行以下操作:
1.启动SQL*Plus或SQL Developer,并以用户sh登录数据库

2.创建表customers_test并收集统计信息:

SQL> DROP TABLE customers_test;

Table dropped.

SQL> CREATE TABLE customers_test AS SELECT * FROM customers;

Table created.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'customers_test');

PL/SQL procedure successfully completed.

3.启用工作量监控
在不同的SQL*Plus会话中,以sys用户登录并执行以下的PL/SQL程序来启用监控300秒:

SQL> BEGIN
  2  DBMS_STATS.SEED_COL_USAGE(null,null,300);
  3  END;
  4  /

PL/SQL procedure successfully completed.

4.以用户sh来在使用工作量的情况下对两个查询解析它们的执行计划。

SQL> EXPLAIN PLAN FOR
  2  SELECT *
  3  FROM customers_test
  4  WHERE cust_city = 'Los Angeles'
  5  AND cust_state_province = 'CA'
  6  AND country_id = 52790;

Explained.

SQL> SELECT PLAN_TABLE_OUTPUT
  2  FROM TABLE(DBMS_XPLAN.DISPLAY('plan_table', null,'basic rows'));
Plan hash value: 2112738156

----------------------------------------------------
| Id  | Operation         | Name           | Rows  |
----------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |
|   1 |  TABLE ACCESS FULL| CUSTOMERS_TEST |     1 |
----------------------------------------------------

8 rows selected.

SQL> EXPLAIN PLAN FOR
  2  SELECT country_id, cust_state_province, count(cust_city)
  3  FROM customers_test
  4  GROUP BY country_id, cust_state_province;

Explained.

SQL> SELECT PLAN_TABLE_OUTPUT
  2  FROM TABLE(DBMS_XPLAN.DISPLAY('plan_table', null,'basic rows'));
Plan hash value: 1820398555

-----------------------------------------------------
| Id  | Operation          | Name           | Rows  |
-----------------------------------------------------
|   0 | SELECT STATEMENT   |                |  1949 |
|   1 |  HASH GROUP BY     |                |  1949 |
|   2 |   TABLE ACCESS FULL| CUSTOMERS_TEST | 55500 |
-----------------------------------------------------

9 rows selected.

第一个执行计划显示基数为1而查询返回932行记录,第二个执行计划显示基数为1949而查询返回145行记录。

5.可选操作,检查对表customers_test所记录的列使用信息

SQL> SET LONG 100000
SQL> SET LINES 120
SQL> SET PAGES 0
SQL> SELECT DBMS_STATS.REPORT_COL_USAGE(user, 'customers_test')
  2  FROM DUAL;
LEGEND:
.......

EQ         : Used in single table EQuality predicate
RANGE      : Used in single table RANGE predicate
LIKE       : Used in single table LIKE predicate
NULL       : Used in single table is (not) NULL predicate
EQ_JOIN    : Used in EQuality JOIN predicate
NONEQ_JOIN : Used in NON EQuality JOIN predicate
FILTER     : Used in single table FILTER predicate
JOIN       : Used in JOIN predicate
GROUP_BY   : Used in GROUP BY expression
...............................................................................

###############################################################################

COLUMN USAGE REPORT FOR SH.CUSTOMERS_TEST
.........................................

1. COUNTRY_ID                          : EQ
2. CUST_CITY                           : EQ
3. CUST_STATE_PROVINCE                 : EQ
4. (CUST_CITY, CUST_STATE_PROVINCE,
    COUNTRY_ID)                        : FILTER
5. (CUST_STATE_PROVINCE, COUNTRY_ID)   : GROUP_BY
###############################################################################

在上面的报告中,前三个列是第一个监控查询中等值谓词中所使用的三个列:

...
WHERE cust_city = 'Los Angeles'
AND cust_state_province = 'CA'
AND country_id = 52790;

所有三个列出现在相同的where子句中,因此报告显示他们作为一组。在第二个查询中,两个列出现在group by子句中,因此报告标记它们作为group_by。在filter与group_by中的列组就是列组的候选者。

在工作量监控下创建所检测到的列组
可以使用dbms_stats.create_extended_stats函数来为执行dbms_stats.seed_col_usage所检测到的列组来创建列组,具体操作如下:
1.基于在监控窗口期间所捕获到的列使用信息来为customers_test表创建列组,执行下面的查询

SQL> SELECT DBMS_STATS.CREATE_EXTENDED_STATS(user, 'customers_test') FROM DUAL;
###############################################################################

EXTENSIONS FOR SH.CUSTOMERS_TEST
................................

1. (CUST_CITY, CUST_STATE_PROVINCE,
    COUNTRY_ID)                        : SYS_STUMZ$C3AIHLPBROI#SKA58H_N created
2. (CUST_STATE_PROVINCE, COUNTRY_ID)   : SYS_STU#S#WF25Z#QAHIHE#MOFFMM_ created
###############################################################################

数据库将为customers_test表创建两个列组:一个列组是过滤谓词,一个列组是group by操作。

2.重新收集表统计信息

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(user,'customers_test');

PL/SQL procedure successfully completed.

3.以用户sh来查询user_tab_col_statistics视图来判断数据库创建了那些额外统计信息:

SQL> SELECT COLUMN_NAME, NUM_DISTINCT, HISTOGRAM
  2  FROM USER_TAB_COL_STATISTICS
  3  WHERE TABLE_NAME = 'CUSTOMERS_TEST'
  4  ORDER BY 1;

COUNTRY_ID                                                   19 FREQUENCY
CUST_CITY                                                   620 HYBRID
CUST_CITY_ID                                                620 NONE
CUST_CREDIT_LIMIT                                             8 NONE
CUST_EFF_FROM                                                 1 NONE
CUST_EFF_TO                                                   0 NONE
CUST_EMAIL                                                 1699 NONE
CUST_FIRST_NAME                                            1300 NONE
CUST_GENDER                                                   2 NONE
CUST_ID                                                   55500 NONE
CUST_INCOME_LEVEL                                            12 NONE
CUST_LAST_NAME                                              908 NONE
CUST_MAIN_PHONE_NUMBER                                    51344 NONE
CUST_MARITAL_STATUS                                          11 NONE
CUST_POSTAL_CODE                                            623 NONE
CUST_SRC_ID                                                   0 NONE
CUST_STATE_PROVINCE                                         145 FREQUENCY
CUST_STATE_PROVINCE_ID                                      145 NONE
CUST_STREET_ADDRESS                                       49900 NONE
CUST_TOTAL                                                    1 NONE
CUST_TOTAL_ID                                                 1 NONE
CUST_VALID                                                    2 NONE
CUST_YEAR_OF_BIRTH                                           75 NONE
SYS_STU#S#WF25Z#QAHIHE#MOFFMM_                              145 NONE
SYS_STUMZ$C3AIHLPBROI#SKA58H_N                              620 HYBRID

25 rows selected.

上面的查询显示了由dbms_stats.create_extended_stats函数所返回的两个列组名。为CUST_CITY, CUST_STATE_PROVINCE和COUNTRY_ID列所创建的列组有一个HYBRID类型的直方图统计信息。

4.再次解析之前的两个查询语句的执行计划

SQL> EXPLAIN PLAN FOR
  2  SELECT *
  3  FROM customers_test
  4  WHERE cust_city = 'Los Angeles'
  5  AND cust_state_province = 'CA'
  6  AND country_id = 52790;

Explained.

SQL> SELECT PLAN_TABLE_OUTPUT
  2  FROM TABLE(DBMS_XPLAN.DISPLAY('plan_table', null,'basic rows'));
Plan hash value: 2112738156

----------------------------------------------------
| Id  | Operation         | Name           | Rows  |
----------------------------------------------------
|   0 | SELECT STATEMENT  |                |   874 |
|   1 |  TABLE ACCESS FULL| CUSTOMERS_TEST |   874 |
----------------------------------------------------

8 rows selected.

SQL> EXPLAIN PLAN FOR
  2  SELECT country_id, cust_state_province, count(cust_city)
  3  FROM customers_test
  4  GROUP BY country_id, cust_state_province;

Explained.

SQL> SELECT PLAN_TABLE_OUTPUT
  2  FROM TABLE(DBMS_XPLAN.DISPLAY('plan_table', null,'basic rows'));
Plan hash value: 1820398555

-----------------------------------------------------
| Id  | Operation          | Name           | Rows  |
-----------------------------------------------------
|   0 | SELECT STATEMENT   |                |   145 |
|   1 |  HASH GROUP BY     |                |   145 |
|   2 |   TABLE ACCESS FULL| CUSTOMERS_TEST | 55500 |
-----------------------------------------------------

9 rows selected.

第一个查询评估的基数是874,要返回的记录数是932,第二个查询评估的基数是145,要返回的记录数是145,这样基数评估的记录数与实际返回的记录已经非常接近了,这就是列组统计信息所带来的好处。

手动创建与收集列组统计信息
在有些情况下,可能知道想要创建的列组。dbms_stats.gather_table_stats函数的method_opt参数可以自动创建与收集列组统计信息。可以通过使用for columns来指定列组从而来创建一个新的列组。

假设存在以下情况:
.想要对sh.customers表上的cust_state_province与country_id列创建列组。

.想要对sh.customers表与新的列组收集统计信息。

手动创建与收集列组统计信息执行以下操作:
1.启动SQL*Plus并以sh用户登录数据库。

2.使用以下PL/SQL程序来创建列组并收集统计信息:

SQL> BEGIN
  2  DBMS_STATS.GATHER_TABLE_STATS( 'sh','customers',
  3  METHOD_OPT => 'FOR ALL COLUMNS SIZE SKEWONLY ' ||
  4  'FOR COLUMNS SIZE SKEWONLY (cust_state_province,country_id)' );
  5  END;
  6  /

PL/SQL procedure successfully completed.

显示列组信息
为了获得列组名,可以使用dbms_stats.show_extended_stats_name函数或数据库视图。也可以使用视图来获得信息比如,distinct值的数量与列组是否有直方图统计信息。
1.启动SQL*Plus并以sh用户登录数据库。

2.为了获得列组名,执行以下PL/SQL程序

SQL> SELECT SYS.DBMS_STATS.SHOW_EXTENDED_STATS_NAME( 'sh','customers',
  2  '(cust_state_province,country_id)' ) col_group_name
  3  FROM DUAL;

COL_GROUP_NAME
------------------------------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM_

查询user_stat_extensions视图

SQL> SELECT EXTENSION_NAME, EXTENSION
  2  FROM USER_STAT_EXTENSIONS
  3  WHERE TABLE_NAME='CUSTOMERS';
EXTENSION_NAME                                                                                                                   EXTENSION
-----------------------------------------------------------------------                                                          ------------------------------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM_                                                                                                   ("CUST_STATE_PROVINCE","COUNTRY_ID")

3.查询创建的列组的distinct值的数量并查看是否创建了直方图

SQL> SELECT e.EXTENSION col_group, t.NUM_DISTINCT, t.HISTOGRAM
  2  FROM USER_STAT_EXTENSIONS e, USER_TAB_COL_STATISTICS t
  3  WHERE e.EXTENSION_NAME=t.COLUMN_NAME
  4  AND e.TABLE_NAME=t.TABLE_NAME
  5  AND t.TABLE_NAME='CUSTOMERS';

COL_GROUP                                                                        NUM_DISTINCT HISTOGRAM
----------------------------------------------------------------------           ------------ ---------
("CUST_STATE_PROVINCE","COUNTRY_ID")                                                      145 FREQUENCY

删除列组
可以使用dbms_stats.drop_extended_stats函数来从表中删除列组

SQL> BEGIN
  2  DBMS_STATS.DROP_EXTENDED_STATS( 'sh', 'customers',
  3  '(cust_state_province, country_id)' );
  4  END;
  5  /

PL/SQL procedure successfully completed.

Oracle 12CR2查询转换之表扩展

在表扩展中,对于读取一个分区表部分数据时优化器会生成使用索引的执行计划。基于索引执行计划可以提高性能,但索引维护会增加开锁。在许多数据库中,DML只影响小部分数据。对于频繁更新的表表扩展使用基于索引的执行计划。你可以在以读取为主的数据上创建一个索引,在以频繁变化的数据上消除索引开销。通过这种方式,表扩展在避免索引维护的同时提高了性能。

表扩展工作原理
表分区使用表扩展成为可能。如果在一个分区表上创建一个本地索引,那么优化器可能会标记索引对于特定的分区不可使用。实际有些分区没有创建索引。在表扩展中,优化器将查询转换为一个union all语句,让一些子查询访问创建索引的分区,一些子查询访问没有创建索引的分区。优化器可以为每个分区选择最有效的访问路径,而不管它是否存在于查询所要访问的所有分区中。

优化器不总是会选择表扩展
.表扩展是基于成本
当数据库访问扩展表的每个分区只会跨越union all的所有分支一次,数据库所连接的任何表都是在分支中被访问。

.语义问题可能导致表扩展无效
例如,一个表出现在一个外连接的右边对于表扩展来说是无效的。

可以使用expand_table hint来控制表扩展。这个hint会覆盖基于成本的决策,但不会覆盖语义检查。

表扩展使用场景
优化器基于查询中出现的谓词条件对每个表必须被访问的分区保持跟踪。分区裁剪能让优化器使用表扩展来生成更有效的执行计划。

下面的例子假设满足以下条件:
.想要对sh.sales表执行星型查询,表sh.sales是基于time_id列进行范围分区的一个分区表。

.想要禁用特定分区上的索引来查看表扩展的优点。

操作步骤如下:
1.以sh用户登录数据库

[oracle@jytest1 ~]$ sqlplus sh/*****@jypdb

SQL*Plus: Release 12.2.0.1.0 Production on Wed Oct 31 18:09:54 2018

Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Last Successful login time: Wed Oct 24 2018 17:00:11 +08:00

Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL> 

2.执行以下查询

SQL> select *  from sales where time_id >= to_date('2000-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') and prod_id = 38;
...........
        38       2470 24-DEC-01             2        999             1       31.47
        38      13440 24-DEC-01             2        999             1       31.47
        38        490 28-DEC-01             2        999             1       31.47
        38       8406 28-DEC-01             2        999             1       31.47
        38       1466 31-DEC-01             3        351             1       31.47
        38       4340 31-DEC-01             3        351             1       31.47
        38      10658 31-DEC-01             3        351             1       31.47
        38      11390 31-DEC-01             3        351             1       31.47
        38      23226 31-DEC-01             3        351             1       31.47

4224 rows selected.

3.查询执行计划

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced allstats last runstats_last peeked_binds'));
SQL_ID  214qgysqqz0k8, child number 0
-------------------------------------
select *  from sales where time_id >= to_date('2000-01-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss') and prod_id = 38

Plan hash value: 2342444420

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | Pstart| Pstop | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |                |      1 |        |       |   224 (100)|          |       |       |   4224 |00:00:00.03 |     334 |
|   1 |  PARTITION RANGE ITERATOR                  |                |      1 |   5078 |   143K|   224   (0)| 00:00:01 |    13 |    28 |   4224 |00:00:00.03 |     334 |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES          |     16 |   5078 |   143K|   224   (0)| 00:00:01 |    13 |    28 |   4224 |00:00:00.02 |     334 |
|   3 |    BITMAP CONVERSION TO ROWIDS             |                |      8 |        |       |            |          |       |       |   4224 |00:00:00.01 |      24 |
|*  4 |     BITMAP INDEX SINGLE VALUE              | SALES_PROD_BIX |      8 |        |       |            |          |    13 |    28 |      8 |00:00:00.01 |      24 |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

   1 - SEL$1
   2 - SEL$1 / SALES@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.2.0.1')
      DB_VERSION('12.2.0.1')
      ALL_ROWS
      NO_PARALLEL
      OUTLINE_LEAF(@"SEL$1")
      BITMAP_TREE(@"SEL$1" "SALES"@"SEL$1" AND(("SALES"."PROD_ID")))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "SALES"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   4 - access("PROD_ID"=38)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   2 - "PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   3 - "SALES".ROWID[ROWID,10], "PROD_ID"[NUMBER,22]
   4 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 7920], "PROD_ID"[NUMBER,22]

Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold


58 rows selected.

在执行计划中的Pstart与Pstop列,显示了优化器判断只需要访问表的13到28分区。在优化器已经判断了被访问的分区之后,它将考虑所有这些分区上可以使用的索引。在上面的执行计划中,优化器选择使用sales_prod_bix位图索引

4.禁用sales表中sales_1995分区上的索引;

SQL> alter index sales_prod_bix modify partition sales_1995 unusable;

Index altered.

5.再次执行之前的查询语句,然后显示执行计划,可以看到执行计划变成了由两个子查询组成的union all语句,第一个子查询还是对13-28分区使用索引,第二个子查询步骤对应的Pstart与Pstop为invalid,id=11的过滤条件为”PROD_ID”=38,id=9的过滤条件为”SALES”.”TIME_ID”=TO_DATE(‘ 2000-01-01 00:00:00’, ‘syyyy-mm-dd hh24:mi:ss’)))这个过滤条件是为否的,所以过滤后的记录为0,从对应的A-Rows列也可以看到记录为0

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced allstats last runstats_last peeked_binds'));
SQL_ID  214qgysqqz0k8, child number 0
-------------------------------------
select *  from sales where time_id >= to_date('2000-01-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss') and prod_id = 38

Plan hash value: 238952339

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                     | Name           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | Pstart| Pstop | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                              |                |      1 |        |       |   224 (100)|          |       |       |   4224 |00:00:00.05 |     334 |
|   1 |  VIEW                                         | VW_TE_2        |      1 |   5079 |   431K|   224   (0)| 00:00:01 |       |       |   4224 |00:00:00.05 |     334 |
|   2 |   UNION-ALL                                   |                |      1 |        |       |            |          |       |       |   4224 |00:00:00.05 |     334 |
|   3 |    PARTITION RANGE ITERATOR                   |                |      1 |   5078 |   143K|   224   (0)| 00:00:01 |    13 |    28 |   4224 |00:00:00.03 |     334 |
|   4 |     TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | SALES          |     16 |   5078 |   143K|   224   (0)| 00:00:01 |    13 |    28 |   4224 |00:00:00.02 |     334 |
|   5 |      BITMAP CONVERSION TO ROWIDS              |                |      8 |        |       |            |          |       |       |   4224 |00:00:00.01 |      24 |
|*  6 |       BITMAP INDEX SINGLE VALUE               | SALES_PROD_BIX |      8 |        |       |            |          |    13 |    28 |      8 |00:00:00.01 |      24 |
|*  7 |    FILTER                                     |                |      1 |        |       |            |          |       |       |      0 |00:00:00.01 |       0 |
|   8 |     PARTITION RANGE EMPTY                     |                |      0 |      1 |    29 |     1   (0)| 00:00:01 |INVALID|INVALID|      0 |00:00:00.01 |       0 |
|*  9 |      TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES          |      0 |      1 |    29 |     1   (0)| 00:00:01 |INVALID|INVALID|      0 |00:00:00.01 |       0 |
|  10 |       BITMAP CONVERSION TO ROWIDS             |                |      0 |        |       |            |          |       |       |      0 |00:00:00.01 |       0 |
|* 11 |        BITMAP INDEX SINGLE VALUE              | SALES_PROD_BIX |      0 |        |       |            |          |INVALID|INVALID|      0 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

   1 - SET$D0A14387   / VW_TE_2@SEL$0A5B0FFE
   2 - SET$D0A14387
   3 - SET$D0A14387_1
   4 - SET$D0A14387_1 / SALES@SEL$1
   7 - SET$D0A14387_2
   9 - SET$D0A14387_2 / SALES@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.2.0.1')
      DB_VERSION('12.2.0.1')
      ALL_ROWS
      NO_PARALLEL
      OUTLINE_LEAF(@"SET$D0A14387_2")
      OUTLINE_LEAF(@"SET$D0A14387_1")
      OUTLINE_LEAF(@"SET$D0A14387")
      EXPAND_TABLE(@"SEL$1" "SALES"@"SEL$1")
      OUTLINE_LEAF(@"SEL$0A5B0FFE")
      OUTLINE(@"SET$D0A14387")
      EXPAND_TABLE(@"SEL$1" "SALES"@"SEL$1")
      OUTLINE(@"SEL$1")
      NO_ACCESS(@"SEL$0A5B0FFE" "VW_TE_2"@"SEL$0A5B0FFE")
      BITMAP_TREE(@"SET$D0A14387_1" "SALES"@"SEL$1" AND(("SALES"."PROD_ID")))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SET$D0A14387_1" "SALES"@"SEL$1")
      BITMAP_TREE(@"SET$D0A14387_2" "SALES"@"SEL$1" AND(("SALES"."PROD_ID")))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SET$D0A14387_2" "SALES"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   6 - access("PROD_ID"=38)
   7 - filter(NULL IS NOT NULL)
   9 - filter(("SALES"."TIME_ID"=TO_DATE(' 2000-01-01 00:00:00', 'syyyy-mm-dd
              hh24:mi:ss')))
  11 - access("PROD_ID"=38)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "ITEM_1"[NUMBER,22], "ITEM_2"[NUMBER,22], "ITEM_3"[DATE,7], "ITEM_4"[NUMBER,22], "ITEM_5"[NUMBER,22], "ITEM_6"[NUMBER,22], "ITEM_7"[NUMBER,22]
   2 - STRDEF[22], STRDEF[22], STRDEF[7], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[22]
   3 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   4 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   5 - "SALES".ROWID[ROWID,10], "SALES"."PROD_ID"[NUMBER,22]
   6 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 7920], "SALES"."PROD_ID"[NUMBER,22]
   7 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   8 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   9 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
  10 - "SALES".ROWID[ROWID,10], "SALES"."PROD_ID"[NUMBER,22]
  11 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 7920], "SALES"."PROD_ID"[NUMBER,22]

Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold


93 rows selected.

6.禁用分区28上的索引(sales_q4_2003),它是查询需要访问的一个分区:

SQL> alter index sales_prod_bix modify partition sales_q4_2003 unusable;

Index altered.

SQL> alter index sales_time_bix modify partition sales_q4_2003 unusable;

Index altered.

通过禁用查询需要访问分区上的索引,查询将不能再使用这些索引。

7.再次执行查询语句,其执行计划如下,执行计划变成了由三个子查询组成的union all语句,相比之前查询多的第三个子查询对表sales的第28个分区执行全表扫描,这里没有索引可用,因为已经禁用28分区上的索引了。

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced allstats last runstats_last peeked_binds'));
SQL_ID  214qgysqqz0k8, child number 0
-------------------------------------
select *  from sales where time_id >= to_date('2000-01-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss') and prod_id = 38

Plan hash value: 3857158179

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                     | Name           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | Pstart| Pstop | A-Rows |   A-Time   | Buffers | Reads  |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                              |                |      1 |        |       |   225 (100)|          |       |       |   4224 |00:00:00.20 |     334 |     44 |
|   1 |  VIEW                                         | VW_TE_2        |      1 |   5080 |   431K|   225   (0)| 00:00:01 |       |       |   4224 |00:00:00.20 |     334 |     44 |
|   2 |   UNION-ALL                                   |                |      1 |        |       |            |          |       |       |   4224 |00:00:00.19 |     334 |     44 |
|   3 |    PARTITION RANGE ITERATOR                   |                |      1 |   5078 |   143K|   223   (0)| 00:00:01 |    13 |    27 |   4224 |00:00:00.17 |     334 |     44 |
|   4 |     TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | SALES          |     15 |   5078 |   143K|   223   (0)| 00:00:01 |    13 |    27 |   4224 |00:00:00.16 |     334 |     44 |
|   5 |      BITMAP CONVERSION TO ROWIDS              |                |      8 |        |       |            |          |       |       |   4224 |00:00:00.03 |      24 |     16 |
|*  6 |       BITMAP INDEX SINGLE VALUE               | SALES_PROD_BIX |      8 |        |       |            |          |    13 |    27 |      8 |00:00:00.03 |      24 |     16 |
|*  7 |    FILTER                                     |                |      1 |        |       |            |          |       |       |      0 |00:00:00.01 |       0 |      0 |
|   8 |     PARTITION RANGE EMPTY                     |                |      0 |      1 |    29 |     1   (0)| 00:00:01 |INVALID|INVALID|      0 |00:00:00.01 |       0 |      0 |
|*  9 |      TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES          |      0 |      1 |    29 |     1   (0)| 00:00:01 |INVALID|INVALID|      0 |00:00:00.01 |       0 |      0 |
|  10 |       BITMAP CONVERSION TO ROWIDS             |                |      0 |        |       |            |          |       |       |      0 |00:00:00.01 |       0 |      0 |
|* 11 |        BITMAP INDEX SINGLE VALUE              | SALES_PROD_BIX |      0 |        |       |            |          |INVALID|INVALID|      0 |00:00:00.01 |       0 |      0 |
|  12 |    PARTITION RANGE SINGLE                     |                |      1 |      1 |    87 |     2   (0)| 00:00:01 |    28 |    28 |      0 |00:00:00.01 |       0 |      0 |
|* 13 |     TABLE ACCESS FULL                         | SALES          |      1 |      1 |    87 |     2   (0)| 00:00:01 |    28 |    28 |      0 |00:00:00.01 |       0 |      0 |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

   1 - SET$D0A14387   / VW_TE_2@SEL$0A5B0FFE
   2 - SET$D0A14387
   3 - SET$D0A14387_1
   4 - SET$D0A14387_1 / SALES@SEL$1
   7 - SET$D0A14387_2
   9 - SET$D0A14387_2 / SALES@SEL$1
  12 - SET$D0A14387_3
  13 - SET$D0A14387_3 / SALES@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.2.0.1')
      DB_VERSION('12.2.0.1')
      ALL_ROWS
      NO_PARALLEL
      OUTLINE_LEAF(@"SET$D0A14387_3")
      OUTLINE_LEAF(@"SET$D0A14387_2")
      OUTLINE_LEAF(@"SET$D0A14387_1")
      OUTLINE_LEAF(@"SET$D0A14387")
      EXPAND_TABLE(@"SEL$1" "SALES"@"SEL$1")
      OUTLINE_LEAF(@"SEL$0A5B0FFE")
      OUTLINE(@"SET$D0A14387")
      EXPAND_TABLE(@"SEL$1" "SALES"@"SEL$1")
      OUTLINE(@"SEL$1")
      NO_ACCESS(@"SEL$0A5B0FFE" "VW_TE_2"@"SEL$0A5B0FFE")
      BITMAP_TREE(@"SET$D0A14387_1" "SALES"@"SEL$1" AND(("SALES"."PROD_ID")))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SET$D0A14387_1" "SALES"@"SEL$1")
      BITMAP_TREE(@"SET$D0A14387_2" "SALES"@"SEL$1" AND(("SALES"."PROD_ID")))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SET$D0A14387_2" "SALES"@"SEL$1")
      FULL(@"SET$D0A14387_3" "SALES"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   6 - access("PROD_ID"=38)
   7 - filter(NULL IS NOT NULL)
   9 - filter(("SALES"."TIME_ID"=TO_DATE(' 2000-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
  11 - access("PROD_ID"=38)
  13 - filter("PROD_ID"=38)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "ITEM_1"[NUMBER,22], "ITEM_2"[NUMBER,22], "ITEM_3"[DATE,7], "ITEM_4"[NUMBER,22], "ITEM_5"[NUMBER,22], "ITEM_6"[NUMBER,22], "ITEM_7"[NUMBER,22]
   2 - STRDEF[22], STRDEF[22], STRDEF[7], STRDEF[22], STRDEF[22], STRDEF[22], STRDEF[22]
   3 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   4 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   5 - "SALES".ROWID[ROWID,10], "SALES"."PROD_ID"[NUMBER,22]
   6 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 7920], "SALES"."PROD_ID"[NUMBER,22]
   7 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   8 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
   9 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
  10 - "SALES".ROWID[ROWID,10], "SALES"."PROD_ID"[NUMBER,22]
  11 - STRDEF[BM VAR, 10], STRDEF[BM VAR, 10], STRDEF[BM VAR, 7920], "SALES"."PROD_ID"[NUMBER,22]
  12 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]
  13 - "SALES"."PROD_ID"[NUMBER,22], "SALES"."CUST_ID"[NUMBER,22], "SALES"."TIME_ID"[DATE,7], "SALES"."CHANNEL_ID"[NUMBER,22], "SALES"."PROMO_ID"[NUMBER,22],
       "SALES"."QUANTITY_SOLD"[NUMBER,22], "SALES"."AMOUNT_SOLD"[NUMBER,22]

Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold


103 rows selected.

Oracle 12CR2查询转换之cursor-duration临时表

在Oracle12C中为了物化查询的中间结果,Oracle数据库在查询编译时在内存中可能会隐式的创建一个cursor_duration临时表。

Cursor-Duration临时表的作用
复杂查询有时会处理相同查询块多次,这将会增加不必要的性能开锁。为了避免这种问题,Oracle数据库可以在游标生命周期内为查询结果创建临时表并存储在内存中。对于有with子句查询,星型转换与分组集合操作的复杂操作,这种优化增强了使用物化中间结果来优化子查询。在这种方式下,cursor-duration临时表提高了性能并且优化了I/O。

Cursor-Duration临时表工作原理
cursor-definition临时表定义内置在内存中。表定义与游标相关,并且只对执行游标的会话可见。当使用cursor-duration临时表时,数据库将执行以下操作:
1.选择使用cursor-duration临时表的执行计划
2.创建临时表时使用唯一名
3.重写查询引用临时表
4.加载数据到内存中直到没有内存可用,在这种情次品下将在磁盘上创建临时段
5.执行查询,从临时表中返回数据
6.truncate表,释放内存与任何磁盘上的临时段

注意,cursor-duration临时表的元数据只要cursor在内存中就会一直存在于内存中。元数据不会存储在数据字典中这意味着通过数据字典视图将不能查询到,不能显性地删除元数据。上面的场景依赖于可用的内存。对于特定查询,临时表使用PGA内存。

cursor-duration临时表的实现类似于排序。如果没有可用内存,那么数据库将把数据写入临时段。对于cursor-duration临时表,主要差异如下:
.在查询结束时数据库释放内存与临时段而不是当row source不现活动时释放。

.内存中的数据仍然存储在内存中,不像排序数据可能在内存与临时段之间移动。
当数据库使用cursor-duration临时表时,关键字cursor duration memory会出现在执行计划中。

cursor-duration临时表使用场景
一个with查询重复相同子查询多次可能有时使用cursor-duration临时表性能更高,下面的查询使用一个with子句来创建三个子查询块:

SQL> set long 99999
SQL> set linesize 300
SQL> with
  2  q1 as (select department_id, sum(salary) sum_sal from hr.employees group by
  3  department_id),
  4  q2 as (select * from q1),
  5  q3 as (select department_id, sum_sal from q1)
  6  select * from q1
  7  union all
  8  select * from q2
  9  union all
 10  select * from q3;

DEPARTMENT_ID    SUM_SAL
------------- ----------
          100      51608
           30      24900
                    7000
           90      58000
           20      19000
           70      10000
          110      20308
           50     156400
           80     304500
           40       6500
           60      28800
           10       4400
          100      51608
           30      24900
                    7000
           90      58000
           20      19000
           70      10000
          110      20308
           50     156400
           80     304500
           40       6500
           60      28800
           10       4400
          100      51608
           30      24900
                    7000
           90      58000
           20      19000
           70      10000
          110      20308
           50     156400
           80     304500
           40       6500
           60      28800
           10       4400

36 rows selected.

下面是优化转换后的执行计划

SQL> select * from table(dbms_xplan.display_cursor(format=>'basic +rows +cost'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
EXPLAINED SQL STATEMENT:
------------------------
with q1 as (select department_id, sum(salary) sum_sal from hr.employees
group by department_id), q2 as (select * from q1), q3 as (select
department_id, sum_sal from q1) select * from q1 union all select *
from q2 union all select * from q3

Plan hash value: 4087957524

----------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name                       | Rows  | Cost (%CPU)|

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                            |       |     6 (100)|
|   1 |  TEMP TABLE TRANSFORMATION               |                            |       |            |
|   2 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9E08D2_620789C |       |            |
|   3 |    HASH GROUP BY                         |                            |    11 |   276   (2)|
|   4 |     TABLE ACCESS FULL                    | EMPLOYEES                  |   100K|   273   (1)|
|   5 |   UNION-ALL                              |                            |       |            |
|   6 |    VIEW                                  |                            |    11 |     2   (0)|
|   7 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9E08D2_620789C |    11 |     2   (0)|
|   8 |    VIEW                                  |                            |    11 |     2   (0)|
|   9 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9E08D2_620789C |    11 |     2   (0)|
|  10 |    VIEW                                  |                            |    11 |     2   (0)|
|  11 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9E08D2_620789C |    11 |     2   (0)|
----------------------------------------------------------------------------------------------------


26 rows selected.

在上面的执行计划中,在步骤1中的TEMP TABLE TRANSFORMATION指示数据库使用cursor-duration临时表来执行查询。在步骤2中的CURSOR DURATION MEMORY指示数据库使用内存,如果有可用内存,将结果作为临时表SYS_TEMP_0FD9E08D2_620789C来进行存储。如果没有可用内存,那么数据库将临时数据写入磁盘。

Linux 恢复rm -rf命令所删除的达梦数据文件

LINUX系统中被删除的文件,只要其句柄没有被关闭,可以在/proc//fd中找到其对应的文件副本。其中指打开该文件的进程id。利用该方法,结合OS命令,DM7提供失效文件的恢复方案,下面用示例来演示:
1.创建一个表空间cs,给其创建两个数据文件

SQL> create tablespace cs datafile '/dm_home/dmdba/dmdbms/data/jydm/cs1.dbf' size 128,'/dm_home/dmdba/dmdbms/data/jydm/cs.dbf' size 128;
executed successfully
used time: 00:00:21.941. Execute id is 90.

2.找出达梦服务器进程ID

[root@cs1 jydm]# ps -ef | grep dmserver
dmdba     2467     1  0 10月15 ?       00:05:53 /dm_home/dmdba/dmdbms/bin/dmserver /dm_home/dmdba/dmdbms/data/jydm/dm.ini -noconsole
root     18893 18058  0 21:45 pts/1    00:00:00 grep --color=auto dmserver

3.显示达梦服务器进程所打开的文件列表

[root@cs1 jydm]# ls /proc/2467/fd -l
总用量 0
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 0 -> /dev/null
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 1 -> /dm_home/dmdba/dmdbms/log/DmServicejydm.log
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 10 -> /dm_home/dmdba/dmdbms/data/jydm/ROLL.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 11 -> /dm_home/dmdba/dmdbms/data/jydm/MAIN.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 12 -> /dm_home/dmdba/dmdbms/data/jydm/BOOKSHOP.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 13 -> /dm_home/dmdba/dmdbms/data/jydm/DMHR.DBF
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 14 -> pipe:[21853]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 15 -> pipe:[21853]
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 16 -> pipe:[21854]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 17 -> pipe:[21854]
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 18 -> pipe:[21855]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 19 -> pipe:[21855]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 2 -> /dm_home/dmdba/dmdbms/log/DmServicejydm.log
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 20 -> pipe:[21856]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 21 -> pipe:[21856]
lrwx------ 1 dmdba dinstall 64 10月 25 21:12 22 -> socket:[297043807]
lrwx------ 1 dmdba dinstall 64 10月 25 21:12 23 -> /dm_home/dmdba/dmdbms/data/jydm/cs1.dbf
lrwx------ 1 dmdba dinstall 64 10月 25 21:42 24 -> /dm_home/dmdba/dmdbms/data/jydm/cs.dbf
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 3 -> socket:[20041]
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 4 -> socket:[20042]
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 5 -> /dm_home/dmdba/dmdbms/data/jydm/SYSTEM.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 6 -> /dm_home/dmdba/dmdbms/data/jydm/dminst.sys
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 7 -> /dm_home/dmdba/dmdbms/data/jydm/TEMP.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 8 -> /dm_home/dmdba/dmdbms/data/jydm/jydm01.log
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 9 -> /dm_home/dmdba/dmdbms/data/jydm/jydm02.log

4.删除表空间cs中的一个数据文件

[root@cs1 jydm]# rm -rf cs.dbf

5.通过调用系统过程SP_FILE_SYS_CHECK()来手动的对表空间失效文件进行检查。

SQL> call SP_FILE_SYS_CHECK();
DMSQL executed successfully
used time: 0.690(ms). Execute id is 93.

6.在表空间cs中创建一个测试表cs,命令执行出错,显示表空间cs中的cs.dbf文件已经被删除了。

SQL> create table cs(cs_id number) tablespace cs;
create table cs(cs_id number) tablespace cs;
[-3430]:tablespace[CS] file[/dm_home/dmdba/dmdbms/data/jydm/cs.dbf] has been deleted.
used time: 31.462(ms). Execute id is 0.

7.调用系统过程SP_TABLESPACE_PREPARE_RECOVER(tablespace_name)准备进行恢复

SQL> call SP_TABLESPACE_PREPARE_RECOVER('CS');
DMSQL executed successfully
used time: 16.121(ms). Execute id is 95.

8.显示达梦服务器进程所打开的文件列表,可以看到cs.dbf文件被标示为deleted了。

[root@cs1 jydm]# ls /proc/2467/fd -l
总用量 0
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 0 -> /dev/null
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 1 -> /dm_home/dmdba/dmdbms/log/DmServicejydm.log
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 10 -> /dm_home/dmdba/dmdbms/data/jydm/ROLL.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 11 -> /dm_home/dmdba/dmdbms/data/jydm/MAIN.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 12 -> /dm_home/dmdba/dmdbms/data/jydm/BOOKSHOP.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 13 -> /dm_home/dmdba/dmdbms/data/jydm/DMHR.DBF
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 14 -> pipe:[21853]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 15 -> pipe:[21853]
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 16 -> pipe:[21854]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 17 -> pipe:[21854]
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 18 -> pipe:[21855]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 19 -> pipe:[21855]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 2 -> /dm_home/dmdba/dmdbms/log/DmServicejydm.log
lr-x------ 1 dmdba dinstall 64 10月 15 12:23 20 -> pipe:[21856]
l-wx------ 1 dmdba dinstall 64 10月 15 12:23 21 -> pipe:[21856]
lrwx------ 1 dmdba dinstall 64 10月 25 21:12 22 -> socket:[297043807]
lrwx------ 1 dmdba dinstall 64 10月 25 21:12 23 -> /dm_home/dmdba/dmdbms/data/jydm/cs1.dbf
lrwx------ 1 dmdba dinstall 64 10月 25 21:42 24 -> /dm_home/dmdba/dmdbms/data/jydm/cs.dbf (deleted)
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 3 -> socket:[20041]
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 4 -> socket:[20042]
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 5 -> /dm_home/dmdba/dmdbms/data/jydm/SYSTEM.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 6 -> /dm_home/dmdba/dmdbms/data/jydm/dminst.sys
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 7 -> /dm_home/dmdba/dmdbms/data/jydm/TEMP.DBF
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 8 -> /dm_home/dmdba/dmdbms/data/jydm/jydm01.log
lrwx------ 1 dmdba dinstall 64 10月 15 12:23 9 -> /dm_home/dmdba/dmdbms/data/jydm/jydm02.log

9.使用操作系统的cp命令将文件(cs.dbf)复制到原位置

[root@cs1 jydm]# cp /proc/2467/fd/24 /dm_home/dmdba/dmdbms/data/jydm/cs.dbf
[root@cs1 jydm]# ls -lrt
总用量 1365112
drwxr-xr-x 2 dmdba dinstall         6 7月  23 22:12 bak
-rw-r--r-- 1 dmdba dinstall       479 7月  23 22:12 sqllog.ini
-rw-r--r-- 1 dmdba dinstall       633 7月  23 22:13 dm_service.prikey
drwxr-xr-x 2 dmdba dinstall         6 7月  23 22:14 HMAIN
-rw-r--r-- 1 dmdba dinstall       908 7月  23 22:14 dminit20180723221249.log
-rw-r--r-- 1 dmdba dinstall       890 7月  23 22:14 dmarch_example.ini
-rw-r--r-- 1 dmdba dinstall      1966 7月  23 22:14 dmdcr_cfg_example.ini
-rw-r--r-- 1 dmdba dinstall       631 7月  23 22:14 dmdcr_example.ini
-rw-r--r-- 1 dmdba dinstall      2070 7月  23 22:14 dmmal_example.ini
-rw-r--r-- 1 dmdba dinstall      1537 7月  23 22:14 dminit_example.ini
-rw-r--r-- 1 dmdba dinstall      1277 7月  23 22:14 dmmonitor_example.ini
-rw-r--r-- 1 dmdba dinstall      1679 7月  23 22:14 dmtimer_example.ini
-rw-r--r-- 1 dmdba dinstall       288 7月  23 22:14 dmmpp_example.ini
-rw-r--r-- 1 dmdba dinstall      1241 7月  23 22:14 dmwatch_example.ini
-rw-r--r-- 1 dmdba dinstall      2146 7月  23 22:14 dmwatcher_example.ini
-rw-r--r-- 1 dmdba dinstall       522 7月  23 22:14 dmwmon_example.ini
-rw-r--r-- 1 dmdba dinstall       636 7月  23 22:14 sqllog_example.ini
drwxr-xr-x 2 dmdba dinstall         6 7月  23 22:14 trace
-rw-r--r-- 1 dmdba dinstall        12 7月  23 22:14 rep_conflict.log
-rw-r--r-- 1 dmdba dinstall 157286400 7月  23 22:16 BOOKSHOP.DBF
-rw-r--r-- 1 dmdba dinstall 134217728 7月  23 22:16 DMHR.DBF
-rw-r--r-- 1 dmdba dinstall     40859 8月  20 15:20 dm.ini
-rw-r--r-- 1 dmdba dinstall  10485760 10月 15 12:21 TEMP.DBF
-rw-r--r-- 1 dmdba dinstall 268435456 10月 15 12:21 jydm02.log
-rw-r--r-- 1 dmdba dinstall 134217728 10月 25 21:24 MAIN.DBF
-rw-r--r-- 1 dmdba dinstall 134217728 10月 25 21:43 cs1.dbf
-rw-r--r-- 1 dmdba dinstall      7168 10月 25 21:43 dm.ctl
drwxr-xr-x 2 dmdba dinstall      4096 10月 25 21:43 ctl_bak
-rw-r--r-- 1 dmdba dinstall 134217728 10月 25 21:43 ROLL.DBF
-rw-r--r-- 1 dmdba dinstall  22020096 10月 25 21:59 SYSTEM.DBF
-rw-r--r-- 1 dmdba dinstall 268435456 10月 25 21:59 jydm01.log
-rw-r--r-- 1 root  root     134217728 10月 25 21:59 cs.dbf
-rw-r--r-- 1 dmdba dinstall       220 10月 25 21:59 dminst.sys

10.复制成功后,调用系统过程SP_TABLESPACE_RECOVER(ts_name)完成表空间失效文件的恢复。注意,要保证数据文件正确修复,需要保证在SP_TABLESPACE_PREPARE_RECOVER后进行数据文件的复制。

SQL> call SP_TABLESPACE_RECOVER('CS');
DMSQL executed successfully
used time: 46.532(ms). Execute id is 96.

11.再次在表空间cs中创建测试表cs命令成功执行

SQL> create table cs(cs_id number) tablespace cs;
executed successfully
used time: 36.913(ms). Execute id is 97.

到此,在Linux系统中恢复被删除的表空间文件恢复成功了。

Oracle Linux 7设置中文字符集

常安装Linux系统本着最简化安装,会默认使用英文字符集,不会安装中文字符集等其他字符。但是在一些必要情况下需要中文的支持,本文将演示如何在Oracle Linux7在安装中文字符集
1、首先使用locale命令看看当前系统所使用的字符集,可以看到是英文

[root@cs2 ~]# locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

2、再看看系统是否有安装中文字符集的支持

[root@cs2 ~]# locale -a |grep CN
bo_CN
bo_CN.utf8
ug_CN
ug_CN.utf8
zh_CN
zh_CN.gb18030
zh_CN.gb2312
zh_CN.gbk
zh_CN.utf8

3、如果没有的话使用yum安装中文字符集

yum  install ibus-table-chinese-1.4.6-3.el7.noarch

4、按如下方法修改配置文件并重启系统测试

cat /etc/locale.conf

#LANG="en_US.UTF-8"

LANG="zh_CN.gb2312"

5、重新查看字符集,修改成功,也可以用echo命令输出中文,能正常显示了

[root@cs2 ~]# locale
LANG=zh_CN.gb2312
LC_CTYPE="zh_CN.gb2312"
LC_NUMERIC="zh_CN.gb2312"
LC_TIME="zh_CN.gb2312"
LC_COLLATE="zh_CN.gb2312"
LC_MONETARY="zh_CN.gb2312"
LC_MESSAGES="zh_CN.gb2312"
LC_PAPER="zh_CN.gb2312"
LC_NAME="zh_CN.gb2312"
LC_ADDRESS="zh_CN.gb2312"
LC_TELEPHONE="zh_CN.gb2312"
LC_MEASUREMENT="zh_CN.gb2312"
LC_IDENTIFICATION="zh_CN.gb2312"
LC_ALL=