Wednesday, March 5, 2014

Invisible Columns in 12c


Whenever there was a requirement to hide a column (or columns) from the users, DBAs used to create views on the table with the required columns and then allow users to access this view. Now, starting with Oracle Database 12.1, there is an inbuilt functionality and new feature called Invisible Columns to achieve this functionality. This means, a DBA can make changes to a table without interrupting applications that use the table and then make the column visible at a later stage, when the change implementation is completed

Creating and Describing a table


As an example, let's create a table CUSTOMERS with the column CUSTOMER_TYPE being invisible
rajesh@PDB1>
rajesh@PDB1> create table customer
  2  ( customer_id number,
  3    customer_name varchar2(30),
  4    customer_type varchar2(5) INVISIBLE);

Table created.

rajesh@PDB1>

A Describe of this table does not display the column CUSTOMER_TYPE
rajesh@PDB1> desc customer
 Name                      Null?    Type
 ------------------------- -------- ---------------
 CUSTOMER_ID                         NUMBER
 CUSTOMER_NAME                      VARCHAR2(30)

rajesh@PDB1>

The following operations will not display the INVISIBLE columns.

·         SELECT * FROM statements in SQL
·         DESCRIBE commands in SQL*Plus
·         %ROWTYPE attribute declarations in PL/SQL
·         Describes in Oracle Call Interface (OCI)
However, the INVISIBLE column can be accessed by explicitly mentioning the column name.

Inserting, querying and updating records


In order to insert data to the invisible column, the column name should be explicitly mentioned in the INSERT statement:

rajesh@PDB1> insert into customer values(1,'Tom','Gold');
insert into customer values(1,'Tom','Gold')
            *
ERROR at line 1:
ORA-00913: too many values


rajesh@PDB1> insert into customer values(1,'Tom');

1 row created.

rajesh@PDB1> insert into customer(customer_id,customer_name,customer_type)
  2     values(2,'Sofia','Bronz');

1 row created.

rajesh@PDB1> commit;

Commit complete.

rajesh@PDB1>

A SELECT * statement will not display the contents of the INVISIBLE column. But if you mention the column name explicitly, the data will be displayed
rajesh@PDB1> select * from customer ;

CUSTOMER_ID CUSTOMER_NAME
----------- ------------------------------
          1 Tom
          2 Sofia

2 rows selected.

rajesh@PDB1> select customer_id,customer_name,customer_type
  2  from customer ;

CUSTOMER_ID CUSTOMER_NAME                  CUSTO
----------- ------------------------------ -----
          1 Tom
          2 Sofia                          Bronz

2 rows selected.

rajesh@PDB1>

The INVISIBLE columns can be a virtual column, or can be a part of functional indexes and can even be used as a partitioning key also. In our example, we will update the column CUSTOMER_TYPE.
rajesh@PDB1> update customer
  2  set customer_type ='Gold'
  3  where customer_id = 2 ;

1 row updated.

rajesh@PDB1> select customer_id,customer_name,customer_type
  2  from customer ;

CUSTOMER_ID CUSTOMER_NAME                  CUSTO
----------- ------------------------------ -----
          1 Tom
          2 Sofia                          Gold

2 rows selected.

rajesh@PDB1>

Relevant Data Dictionary information


Since we now understand the functionality of the INVISIBLE COLUMNS feature, it might be interesting to also understand how the information is stored internally in the Data Dictionary tables.
When a column is marked as INVISIBLE, the COL# value for that column to 0 in the Data Dictionary COL$ table (and the PROPERTY value is also updated to mark it as invisible). Hence, this is not included in the column order as SELECT * FROM uses this Column Ordering.

rajesh@PDB1>
rajesh@PDB1> select col#,segcol#,name
  2  from sys.col$
  3  where obj# = (select objecT_id
  4  from user_objects where object_name ='CUSTOMER');

      COL#    SEGCOL# NAME
---------- ---------- --------------------
         1          1 CUSTOMER_ID
         2          2 CUSTOMER_NAME
         0          3 CUSTOMER_TYPE

3 rows selected.

rajesh@PDB1>
rajesh@PDB1> select column_name,column_id,hidden_column,user_generated
  2  from user_tab_cols
  3  where table_name ='CUSTOMER' ;

COLUMN_NAME           COLUMN_ID HID USE
-------------------- ---------- --- ---
CUSTOMER_ID                   1 NO  YES
CUSTOMER_NAME                 2 NO  YES
CUSTOMER_TYPE                   YES YES

3 rows selected.

rajesh@PDB1>

Note: SEGCOL# remains the same irrespective of the changes. Invisible columns are User Generated HIDDEN COLUMNS, hence, they can be changed to make them VISIBLE (System generated Hidden Columns cannot be altered). However, note, that this will then change the column id.

rajesh@PDB1>
rajesh@PDB1> alter table customer modify customer_type visible;

Table altered.

rajesh@PDB1>
rajesh@PDB1> select col#,segcol#,name
  2  from sys.col$
  3  where obj# = (select objecT_id
  4  from user_objects where object_name ='CUSTOMER');

      COL#    SEGCOL# NAME
---------- ---------- --------------------
         1          1 CUSTOMER_ID
         2          2 CUSTOMER_NAME
         3          3 CUSTOMER_TYPE

3 rows selected.

rajesh@PDB1> select column_name,column_id,hidden_column,user_generated
  2  from user_tab_cols
  3  where table_name ='CUSTOMER' ;

COLUMN_NAME           COLUMN_ID HID USE
-------------------- ---------- --- ---
CUSTOMER_TYPE                 3 NO  YES
CUSTOMER_NAME                 2 NO  YES
CUSTOMER_ID                   1 NO  YES

3 rows selected.

rajesh@PDB1>

Implications when changing visibility of columns in a table


Whenever the existing columns in a table are made INVISIBLE/VISIBLE, the Column Ordering of the table can change (depending on the position of the column being altered). Hence a DBA needs to be cautious changing the columns to VISIBLE and INVISIBLE. From an application perspective, it's always recommended to have fully qualified column names in the SELECT query.

rajesh@PDB1> select * from customer ;

CUSTOMER_ID CUSTOMER_NAME                  CUSTO
----------- ------------------------------ -----
          1 Tom
          2 Sofia                          Gold

2 rows selected.

rajesh@PDB1> alter table customer modify customer_name invisible;

Table altered.

rajesh@PDB1> select column_name,column_id,hidden_column,user_generated
  2  from user_tab_cols
  3  where table_name ='CUSTOMER';

COLUMN_NAME           COLUMN_ID HID USE
-------------------- ---------- --- ---
CUSTOMER_ID                   1 NO  YES
CUSTOMER_NAME                   YES YES
CUSTOMER_TYPE                 2 NO  YES

3 rows selected.

rajesh@PDB1> alter table customer modify customer_name visible;

Table altered.

rajesh@PDB1> select column_name,column_id,hidden_column,user_generated
  2  from user_tab_cols
  3  where table_name ='CUSTOMER';

COLUMN_NAME           COLUMN_ID HID USE
-------------------- ---------- --- ---
CUSTOMER_TYPE                 2 NO  YES
CUSTOMER_NAME                 3 NO  YES
CUSTOMER_ID                   1 NO  YES

3 rows selected.

rajesh@PDB1>
rajesh@PDB1> select * from customer;

CUSTOMER_ID CUSTO CUSTOMER_NAME
----------- ----- ------------------------------
          1       Tom
          2 Gold  Sofia

2 rows selected.


rajesh@PDB1>