A virtual index is a “fake” index whose definition exists in the data dictionary, but has no associated index segment. The purpose of virtual indexes is to simulate the existence of an index – without actually building a full index. This allows developers to run an explain plan as if the index is present without waiting for the index creation to complete and without using additional disk space. If we observe that optimizer is creating a plan which is expensive and SQL tuning advisor suggest us to create an index on a column, in case of production database it may not be always feasible to create an index and test the changes. We need to make sure that the created index will not have any negative impact on the execution plan of other queries running in the database.
Lets test it 😊
CREATE A TEST TABLE
SQL> select count(1) from dba_objects;
COUNT(1)
----------
52220
SQL> create sequence sys.sequencetest cache 10000;
Sequence created.
SQL> alter session enable parallel dml;
Session altered.
SQL> alter session enable parallel ddl;
Session altered.
SQL> set timing on;
SQL>
SQL> create /*+ parallel */ table sys.test as
2 select sequencetest.nextval ID, d.*
3 from dba_objects d, (select 1 from dba_objects where rownum < 500)r;
Table created.
Elapsed: 00:00:57.39
SQL>
SQL> select count(1) from sys.test;
COUNT(1)
----------
26057281
SQL> select ds.owner, ds.segment_name, ds.bytes/1024/1024/1024 size_gb from dba_segments ds where ds.segment_name='TEST';
OWNER SEGMENT_NAME SIZE_GB
-------------------- -------------------- ----------
SYS TEST 3.84020996
SELECT ANY VALUE FROM THE TABLE
SQL> select * from sys.test where object_name = 'STANDARD';
CHECK THE EXPLAIN PLAN FOR THE SELECT QUERY
SQL> set autotrace traceonly explain
SQL>
SQL> select * from sys.test where object_name = 'STANDARD';
CREATE A VIRTUAL INDEX ON THE TABLE CREATED
Remember, in order to create a virtual index you need to specify the NOSEGMENT clause in the CREATE INDEX statement. Also note by executing the above statement, an index segment is not created.
SQL> create index test_index on sys.test (object_name) NOSEGMENT;
Index created.
SQL>
GENERATE SOME STATISTICS OF YOUR VIRTUAL INDEXES
SQL> exec dbms_stats.generate_stats('SYS', 'TEST_INDEX');
CHECK DBA_INDEXES AND DBA_OBJECTS
So, The object exists in database, but we dont have segment for the same.
SQL> set autotrace off
SQL>
SQL> select index_name from dba_indexes where table_name = 'TEST' and index_name = 'TEST_INDEX';
no rows selected
SQL>
SQL> col OBJECT_NAME format a20;
SQL>
SQL> select object_name, object_type from dba_objects where object_name = 'TEST_INDEX';
OBJECT_NAME OBJECT_TYPE
-------------------- -----------------------
TEST_INDEX INDEX
CHECK AGAIN THE EXPLAIN PLAN FOR THE SELECT QUERY
We can clearly observe that the index is not being used.
SQL> set autotrace traceonly explain
SQL>
SQL> select * from sys.test where object_name = 'STANDARD';
TO MAKE USE OF THE VIRTUAL INDEX CREATED, WE NEED TO SET _USE_NOSEGMENT_INDEXES PARAMETER TO TRUE
SQL> alter session set "_use_nosegment_indexes" = true;
Session altered.
SQL>
CHECK AGAIN THE EXPLAIN PLAN FOR THE SELECT QUERY
The cost of the query has decreased to 5 from 72076.
This saves so much time. After checking your execution plans, if you are ok with them you can actually create the index.
To create this virtual index as a real one, you must drop it and re-create it without NOSEGMENT clause.
SQL> select * from sys.test where object_name = 'STANDARD';
Once you set this hidden parameter, the optimizer will start using the virtual index you created on this table.
If you run this query from any other session, it will not use this virtual index (as we have used “alter session” statement)
Notes
- It can be used only in Enterprise Edition not Standard Edition. Standard Edition has no deferred segment creation so you cannot create a data object without data segment.
- You can analyze virtual indexes.
- You cannot rebuild a virtual index; it throws an ORA-8114: “User attempted to alter a fake index”
- You can drop the index just as a normal index.
SQL> drop index <index_name>;
DROP TEST TABLE & INDEX & SEQUENCE
SQL> drop table sys.test cascade constraints;
Table dropped.
SQL> drop sequence sequencetest;
Sequence dropped.