oracle - 为什么Oracle要在这里添加隐藏列?

标签 oracle oracle12c

我们最近将客户系统迁移到 Oracle 12c 和我们产品的最新版本。此过程包括运行大量迁移脚本,这些脚本主要是添加或更改表。我们注意到向表添加列的同时还提供默认值,会创建一个额外的隐藏列 SYS_NC00002$ .

您应该能够使用以下代码重现这一点

create table xxx (a integer);
alter table xxx add (b integer default 1);

select table_name, column_name, data_type, data_length, column_id, default_length, data_default from user_tab_cols where table_name='XXX';

Table_Name|column_Name |data_Type|data_Length|column_Id|default_Length|data_Default|
------------------------------------------------------------------------------------
XXX       |A           |NUMBER   |         22|        1|              |            |
XXX       |SYS_NC00002$|RAW      |        126|         |              |            |
XXX       |B           |NUMBER   |         22|        2|             1|1           |

当我填充表格并查看该隐藏列中的值时,它们都是相同的:
select distinct SYS_NC00002$ from xxx;

Sys_Nc00002$|
-------------
01          |

令人惊讶的是,当我没有立即设置默认值而是在额外的语句中时,不会创建额外的隐藏列。
create table xxy (a integer);
alter table xxy add (b integer);
alter table xxy modify b default 1;

select table_name, column_name, data_type, data_length, column_id, default_length, data_default from user_tab_cols where table_name='XXY';

Table_Name|column_Name|data_Type|data_Length|column_Id|default_Length|data_Default|
-----------------------------------------------------------------------------------
XXY       |A          |NUMBER   |         22|        1|              |            |
XXY       |B          |NUMBER   |         22|        2|             1|1           |

谁能解释这个隐藏列的用途以及为什么它只在第一个示例中创建,而不是在第二个示例中创建?

最佳答案

在 Oracle 版本 11g 中,Oracle 引入了一种新的优化技术来提高 DDL 操作的性能。当添加 时,此新功能允许极快的执行时间。非空 具有默认值的列到现有表。自 12c 版以来,DDL 优化已扩展到包括 具有默认值的列。

考虑以下具有 1.000.000 行的测试表:

sql> create table xxy
as select rownum a from dual connect by level <= 1e6
;
sql> select /*+ gather_plan_statistics */ count(1) from xxy;
sql> select * from table(dbms_xplan.display_cursor); 

现在我们将在 11g 和 12c 的不同 session 中添加一个具有默认值的额外非空列:
11g> alter table xxy add b number default 1;
     --Table XXY altered. Elapsed: 00:01:00.998

12c> alter table xxy add b number default 1;
     --Table XXY altered. Elapsed: 00:00:00.052

注意执行时间的差异:1M 行在 5 毫秒内更新!?

执行计划显示:
11g> select count(1) from xxy where b = 1;
  COUNT(1)
----------
   1000000
11g> select * from table(dbms_xplan.display_cursor);
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |  1040 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|*  2 |   TABLE ACCESS FULL| XXY  |   898K|    11M|  1040   (1)| 00:00:13 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("B"=1)
Note
-----
   - dynamic sampling used for this statement (level=2)

12c> select count(1) from xxy where b = 1;
12c> select * from table(dbms_xplan.display_cursor);
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   429 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|*  2 |   TABLE ACCESS FULL| XXY  |  1000K|  4882K|   429   (2)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(DECODE(TO_CHAR(SYS_OP_VECBIT("SYS_NC00002$",0)),NULL,NVL("
              B",1),'0',NVL("B",1),'1',"B")=1)
Note
-----
   - statistics feedback used for this statement

与 11g 相比,12c 上的执行计划显示了一个复杂的谓词部分,涉及一个新的内部列 SYS_NC00006$。 .

该谓词表明,在内部,Oracle 仍在考虑 B 列可能包含非默认值。这意味着 - Oracle 首先不会使用默认值物理更新每一行。

为什么要新的内部专栏 SYS_NC00006$被 build ?
12c> select column_name, virtual_column, hidden_column, user_generated 
from user_tab_cols
where table_name = 'XXY'
;
COLUMN_NAME      VIR HID USE
---------------- --- --- ---
B                NO  NO  YES
SYS_NC00002$     NO  YES NO 
A                NO  NO  YES

12c> select a, b, SYS_NC00002$ hid from xxy where a in (1,10);

        A          B HID            
---------- ---------- ----------------
         1          1                 
        10          1                 

12c> update xxy set b=1 where a=10 and b=1;
1 row updated.

12c> select a, b, SYS_NC00002$ hid from xxy where a in (1,10);
         A          B HID            
---------- ---------- ----------------
         1          1                 
        10          1 01              

请注意 B 和相关内部列的值的差异。 Oracle 只是检查其系统生成的内部列(例如 SYS_NC00006$ )并通过 SYS_OP_VECBIT函数是否考虑 B 列的默认值或通过显式 DML 语句修改的实际值。

两个单独的 alter 语句是什么?
12c> alter table xxy add (b integer);
12c> alter table xxy modify b default 1;

12c> select count(b), count(coalesce(b,0)) nulls  from xxy where b = 1 or b is null;

  COUNT(B)      NULLS
---------- ----------
         0    1000000

对于所有行,新列的值保持为 NULL。不需要真正的更新,因此不会优化 DDL 语句。

Here是 OTN 文章,更详细地解释了新的 DDL 优化。

关于oracle - 为什么Oracle要在这里添加隐藏列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45756882/

相关文章:

oracle - 错误地违反了 PL/SQL Pragma

oracle - groovy.sql.Sql 使用 Oracle 10g 调用firstRow 时出错

Oracle:通过 SELECT 语句创建临时表

sql - 查询以返回与电影租赁数据库中观看次数最多的导演配对的用户

java - Apple 弃用 Java。 Apple 特定的定制是否仍可与其他 JRE/JVM 一起使用?

oracle12c - 关于创建实体关系模型的指南的担忧

database - 为什么 Oracle 不在 DBA_dependencies 中包含使用任何数据库链接创建的对象 View ?

sql - Oracle 中带有 CASE 语句的条件 WHERE 子句

oracle11g - 从 11g 数据库导出时出现 Oracle 12c 数据泵错误

plsql - Google OAuth 2.0 服务器到服务器 : Bad request