Почему Oracle добавляет скрытый столбец здесь?

недавно мы перенесли клиентскую систему на 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           |

кто-нибудь может объяснить, что это скрытый столбец и почему он создается только в первом примере, но не во втором?

1 ответов


в Oracle release 11g Oracle внедрила новый метод оптимизации для повышения производительности DDL-операций. Эта новая функция позволяет чрезвычайно быстрое время выполнения при добавлении НЕ NULL столбец со значением по умолчанию для существующей таблицы. Начиная с выпуска 12c оптимизация DDL была расширена, чтобы включить NULL столбцы, имеющие значение по умолчанию.

рассмотрим следующую тестовую таблицу с 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); 

теперь мы собираемся добавить дополнительный столбец not null, имеющий значение по умолчанию в разных сеансах для 11g и 12c:

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

план выполнения на 12c показывает в отличие от 11g сложную часть предиката, включающую новый внутренний столбец SYS_NC00006$.

этот предикат указывает, что внутренне Oracle все еще рассматривает столбец B чтобы иметь возможность содержать значения, отличные от значений по умолчанию. Это означает-Oracle сначала физически не обновляет каждую строку со значением по умолчанию.

почему новый внутренний колонки - это?

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 или реальное значение modiefed через явный оператор 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

значение нового столбца остается нулевым для всех строк. Никакого реального обновления, поэтому нужно DDL будет не оптимизирован.

здесь - это статья OTN, которая более подробно объясняет новую оптимизацию DDL.