Oracle Index Management
What is an Index in Oracle?
An index in Oracle is a performance tuning structure that improves the speed of data retrieval on a table. It works like a lookup—instead of scanning every row, Oracle uses the index to find data faster.
Why Use Indexes?
- Speed up
SELECT
,JOIN
, andWHERE
clauses. - Help enforce uniqueness with unique indexes.
- Reduce I/O for large datasets.
Think of it like an index in a book—jump directly to what you need.
Real-Time Example
-- Without index: full table scan
SELECT * FROM employees WHERE email = 'HR@EXAMPLE.COM';
-- Create index to speed it up
CREATE INDEX emp_email_idx ON employees(email);
Types of Indexes (Just Names Here; Explained Later)
Oracle provides multiple index types to optimize different query patterns. The right index improves performance by reducing I/O and speeding up data access.
Why So Many Types?
Each index type is designed for specific data characteristics or workload patterns.
Knowing the difference helps you create faster and leaner systems.
Common Index Types and Use Cases
Index Type | Description | Best Use Case | Sample Command |
---|---|---|---|
B-tree (default) | Balanced tree structure for fast access to unique or near-unique values. | Primary/foreign keys, searches, sorting | CREATE INDEX idx ON emp(email); |
Bitmap | Uses bitmaps for storage, ideal for low-cardinality data. | Gender, status, Y/N flags in warehouses | CREATE BITMAP INDEX idx ON emp(gender); |
Function-based | Stores result of an expression or function. | Queries using functions in WHERE clause | CREATE INDEX idx ON emp(UPPER(name)); |
Unique | Ensures all values in the indexed column(s) are unique. | Enforcing uniqueness | CREATE UNIQUE INDEX idx ON emp(email); |
Composite | Multi-column index for compound filter conditions. | Filtering on multiple columns | CREATE INDEX idx ON emp(dept_id, name); |
Invisible | Index exists but is ignored by optimizer unless hinted. | Testing index effect without using it | CREATE INDEX idx ON emp(job_id) INVISIBLE; |
Unusable | Index marked as unusable; skipped by optimizer. | During data load or rebuild | ALTER INDEX idx UNUSABLE; |
Compressed | Compresses repeating values in leading columns. | Large indexes with repetitive data | CREATE INDEX idx ON emp(dept_id, job_id) COMPRESS; |
Automatic (19c+) | Created and managed by Oracle automatically. | Let Oracle decide based on workload | Auto-managed by Oracle |
B-tree Clustered | Created on cluster key; shared by all clustered tables. | Clustered tables | CREATE INDEX idx ON CLUSTER emp_cluster; |
Hash Cluster | Used with hash clusters; not created like normal indexes. | Fast equality searches using hash keys | Done via cluster definition |
Global Partitioned | One index spanning all partitions. | Queries accessing multiple partitions | CREATE INDEX idx ON sales(date) GLOBAL; |
Local Partitioned | Index created per partition. | Partition-pruned queries | CREATE INDEX idx ON sales(date) LOCAL; |
Reverse Key | Reverses bytes to prevent index block contention. | RAC/high-insert environments | CREATE INDEX idx ON emp(emp_id) REVERSE; |
Domain | User-defined, for special data like text or spatial. | Text, XML, spatial data types | CREATE INDEX idx ON docs(text) INDEXTYPE IS CTXSYS.CONTEXT; |
Quick Tips
- Use B-tree for most OLTP cases.
- Choose Bitmap for reporting and static data.
- Go Function-based when filters use expressions.
- Make indexes Invisible to test without dropping.
- Compress large indexes to save space.
Guidelines for Managing Indexes in Oracle
Efficient indexing improves query performance, reduces I/O, and optimizes storage. These best practices will help you maintain effective, high-performance indexes.
1. Index Creation Strategy
Create Indexes After Inserting Table Data
Creating indexes after bulk inserts improves performance and avoids unnecessary index maintenance during data load.
-- Load data first
INSERT INTO employees ...
-- Then create index
CREATE INDEX emp_name_idx ON employees(last_name);
Consider Parallel and NOLOGGING Options
Speed up index creation for large tables.
CREATE INDEX emp_idx ON employees(email) PARALLEL 4 NOLOGGING;
2. Choose the Right Columns to Index
Index Only Frequently Queried Columns
Focus on columns used in WHERE
, JOIN
, or ORDER BY
clauses.
Don’t index:
- Columns with few distinct values (e.g., YES/NO)
- Columns rarely used in queries
Order Columns Smartly in Composite Indexes
Put the most selective column first in multi-column indexes.
CREATE INDEX emp_comp_idx ON employees(department_id, job_id);
3. Manage Index Overhead
Limit the Number of Indexes Per Table
Too many indexes = slow DML (INSERT/UPDATE/DELETE).
Keep only what is necessary for performance.
Drop Unused Indexes
Use index usage monitoring to decide what to drop.
-- Start monitoring
ALTER INDEX emp_idx MONITORING USAGE;
-- Check usage
SELECT * FROM V$OBJECT_USAGE WHERE INDEX_NAME = 'EMP_IDX';
-- Drop if unused
DROP INDEX emp_idx;
4. Storage and Tablespace Planning
Specify Tablespace for Indexes
Separate index I/O and improve manageability.
CREATE INDEX emp_idx ON employees(email) TABLESPACE idx_tbs;
Estimate Index Size
Helps plan storage needs and avoid surprises.
-- Use DBMS_SPACE to estimate
SELECT * FROM TABLE(DBMS_SPACE.CREATE_INDEX_COST(...));
5. Special Index Features
Use Unusable Indexes for Large Loads
Mark index unusable to skip maintenance during loads.
ALTER INDEX emp_idx UNUSABLE;
Rebuild after data load:
ALTER INDEX emp_idx REBUILD;
Use Invisible Indexes for Safe Testing
Invisible indexes don’t affect execution plans unless hinted.
CREATE INDEX emp_test_idx ON employees(email) INVISIBLE;
Avoid Duplicate Indexes
Oracle 12.2+ allows multiple indexes on same columns (e.g., B-tree & bitmap), but avoid unless for controlled testing.
6. Index Maintenance and Optimization
Coalesce or Rebuild Fragmented Indexes
- Coalesce: Lightweight; merges leaf blocks.
- Rebuild: Recreates the index from scratch.
ALTER INDEX emp_idx COALESCE;
ALTER INDEX emp_idx REBUILD;
7. Be Careful with Constraints
⚠️ Dropping Constraints Also Drops Indexes
If you drop a unique or primary key constraint, the index is also dropped unless it’s user-defined.
ALTER TABLE employees DROP CONSTRAINT emp_email_uk;
-- Unique index on 'email' may be gone
8. Advanced Considerations
Leverage Deferred Segment Creation
Index segment is not created until data is inserted—saves space for empty tables.
SELECT SEGMENT_CREATED FROM USER_INDEXES WHERE INDEX_NAME = 'EMP_IDX';
Reduce Indexes with In-Memory Column Store
Oracle Database In-Memory eliminates the need for many indexes for analytics.
ALTER TABLE employees INMEMORY;
Summary Table
Area | Guideline Example |
---|---|
Index Timing | Create after data load |
Column Selection | Index frequently queried columns only |
Composite Indexes | Order columns by selectivity |
Usage Monitoring | Use MONITORING USAGE and V$OBJECT_USAGE |
Maintenance | Use COALESCE or REBUILD as needed |
Performance Optimization | Use PARALLEL and NOLOGGING options |
Storage Management | Estimate size, assign to index tablespace |
Advanced Options | Use Invisible, Unusable, or In-Memory as needed |
Constraint Awareness | Be cautious when dropping constraints |
Creating Indexes in Oracle
Indexes boost performance by allowing Oracle to find rows faster. Here’s how to create different types of indexes efficiently, with the right options and use cases.
✅ Prerequisites for Creating Indexes
You must have:
CREATE INDEX
privilege- Enough tablespace (if specified)
- Valid column data types (not LOB directly)
Create an Index Explicitly
Creates a non-unique index.
CREATE INDEX emp_dept_idx ON employees(department_id);
Create a Unique Index
Ensures indexed column(s) contain unique values.
CREATE UNIQUE INDEX emp_email_uk_idx ON employees(email);
🔹 Oracle automatically creates these if a UNIQUE constraint is defined.
Index with a Constraint
Oracle auto-creates index with PRIMARY KEY
or UNIQUE
constraints.
ALTER TABLE employees ADD CONSTRAINT emp_pk PRIMARY KEY (employee_id);
-- Implicitly creates a unique index
Create a Large Index
Use PARALLEL and NOLOGGING to speed up creation and reduce redo.
CREATE INDEX big_sales_idx ON sales(transaction_date) PARALLEL 4 NOLOGGING TABLESPACE idx_tbs;
Create Index Online
Avoid table locking—ideal for production.
CREATE INDEX emp_job_idx ON employees(job_id) ONLINE;
✅ Allows concurrent DML during creation.
Function-Based Index
Pre-computes values of expressions or functions.
CREATE INDEX emp_upper_name_idx ON employees(UPPER(last_name));
🔸 Enables efficient search on transformed values.
Compressed Index
Compresses repeated column values to save space.
CREATE INDEX emp_multi_col_idx ON employees(department_id, job_id) COMPRESS 1;
COMPRESS 1
compresses all but the last column.
Create an Unusable Index
Created but not available for use until rebuilt. Useful for deferred builds.
CREATE INDEX emp_temp_idx ON employees(temp_col) UNUSABLE;
-- Later: ALTER INDEX emp_temp_idx REBUILD;
Create an Invisible Index
Invisible to optimizer unless hinted—great for testing.
CREATE INDEX emp_test_idx ON employees(email) INVISIBLE;
Multiple Indexes on Same Columns
Since Oracle 12.2, allowed in special cases (e.g., different index types).
-- One B-tree, one bitmap (if allowed on same columns)
CREATE INDEX emp_btree_idx ON employees(email);
CREATE BITMAP INDEX emp_bmap_idx ON employees(email);
❗ Use sparingly—Oracle chooses only one during optimization.
Summary Table
Type | Syntax | Use Case |
---|---|---|
Basic | CREATE INDEX | Simple search on columns |
Unique | CREATE UNIQUE INDEX | Enforce uniqueness |
With Constraint | ALTER TABLE ADD CONSTRAINT | Auto-index created |
Function-Based | ON table(FUNC(col)) | Searching on computed values |
Compressed | COMPRESS N | Save space |
Unusable | UNUSABLE | Create but don’t use immediately |
Invisible | INVISIBLE | Testing new indexes |
Large | PARALLEL , NOLOGGING | Fast creation on big data |
Online | ONLINE | Avoids locks |
Multiple on Same Columns | Oracle 12.2+ | Rare, advanced use |
Altering Indexes in Oracle
Indexes aren’t “set and forget”—you may need to rebuild, rename, hide, monitor, or drop them as data and access patterns evolve.
About Altering Indexes
Index changes help optimize performance, reclaim space, or control usage. Oracle allows many runtime adjustments.
Altering Storage of an Index
Change tablespace, storage, or parallel settings:
ALTER INDEX emp_idx REBUILD TABLESPACE fast_idx PARALLEL 2;
Rebuilding an Index
Fix fragmentation or make an unusable index usable.
ALTER INDEX emp_idx REBUILD;
ALTER INDEX emp_idx REBUILD ONLINE;
Make an Index Unusable
Disable without dropping. Good for bulk operations.
ALTER INDEX emp_idx UNUSABLE;
-- Rebuild later when needed
Make Index Invisible/Visible
Invisible indexes are ignored by the optimizer (unless hinted).
ALTER INDEX emp_idx INVISIBLE;
ALTER INDEX emp_idx VISIBLE;
Rename an Index
Change index name without recreating.
ALTER INDEX emp_idx RENAME TO emp_name_idx;
Monitor Index Usage
Track if an index is used in queries.
ALTER INDEX emp_idx MONITORING USAGE;
SELECT * FROM V$OBJECT_USAGE;
Monitor Space Use
Check index size and bloat:
SELECT SEGMENT_NAME, BYTES/1024/1024 AS MB
FROM DBA_SEGMENTS
WHERE SEGMENT_TYPE = 'INDEX' AND SEGMENT_NAME = 'EMP_IDX';
Drop an Index
Remove when no longer needed.
DROP INDEX emp_idx;
⚠ If it’s part of a PRIMARY KEY
or UNIQUE
constraint, drop the constraint first.
Automatic Indexing (Oracle 19c+)
Oracle can auto-create, monitor, and drop indexes using internal intelligence. It’s optional but powerful.
About Automatic Indexing
- Runs in the background (Autonomous or manually enabled)
- Learns query patterns
- Only keeps beneficial indexes
How It Works
- Creates indexes in INVISIBLE mode
- Tests performance
- Promotes or drops based on benefit
Configure Automatic Indexing
Enable or disable:
-- Enable
EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_MODE','IMPLEMENT');
-- Disable
EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_MODE','OFF');
Generate Auto Index Reports
Check what Oracle’s doing:
SELECT DBMS_AUTO_INDEX.REPORT_LAST_ACTIVITY FROM DUAL;
SELECT DBMS_AUTO_INDEX.REPORT_ACTIVITY(SYSDATE-1, SYSDATE) FROM DUAL;
Views for Auto Index Info
View | Description |
---|---|
DBA_AUTO_INDEX_CONFIG | Current config |
DBA_AUTO_INDEXES | All auto-created indexes |
DBA_AUTO_INDEX_TASKS | Execution tasks |
DBA_AUTO_INDEX_STATISTICS | Index benefits/costs |
DBA_AUTO_INDEX_SQL_ACTIONS | SQL statements affected |
Recap: What You Can Do
Action | Command |
---|---|
Rebuild | ALTER INDEX ... REBUILD |
Rename | ALTER INDEX ... RENAME TO |
Make unusable | ALTER INDEX ... UNUSABLE |
Invisible | ALTER INDEX ... INVISIBLE |
Drop | DROP INDEX |
Monitor usage | ALTER INDEX ... MONITORING USAGE |
Use Auto Index | DBMS_AUTO_INDEX.CONFIGURE(...) |
Oracle Indexes Data Dictionary Views
View Name | Scope | Purpose / What It Shows | Common Use Cases / Notes |
---|---|---|---|
DBA_INDEXES | All indexes | Shows metadata about all indexes in the database. | Type, status, uniqueness, tablespace, logging, compression, visibility, etc. |
ALL_INDEXES | Accessible | Same as DBA_INDEXES but limited to tables accessible to the current user. | Use when you don’t have DBA access. |
USER_INDEXES | Owned | Metadata for indexes owned by the current user. | Fastest for personal schema-level queries. |
DBA_IND_COLUMNS | All indexes | Details about columns that compose indexes (name, position, length, order). | Useful to identify which columns are indexed. |
ALL_IND_COLUMNS | Accessible | Indexed column details for all accessible indexes. | Use when working with objects not owned but accessible. |
USER_IND_COLUMNS | Owned | Indexed columns in the user’s own schema. | Used frequently to find column-level index info. |
DBA_IND_PARTITIONS | Partitioned only | Partition-level index info: partition name, storage, stats, tablespace. | Use for managing or analyzing partitioned index structures. |
ALL_IND_PARTITIONS | Accessible | Same as above, for accessible indexes. | |
USER_IND_PARTITIONS | Owned | Partition details for user’s own indexes. | Helps troubleshoot local/global index partitions. |
DBA_IND_EXPRESSIONS | Function-based | Expression definitions used in function-based indexes. | Shows stored expressions like UPPER(column_name) . |
ALL_IND_EXPRESSIONS | Accessible | Same as above, for accessible indexes. | |
USER_IND_EXPRESSIONS | Owned | Expression info for user-created function-based indexes. | Good for review and tuning function-based indexing. |
DBA_IND_STATISTICS | All indexes | Optimizer statistics for indexes (e.g. clustering factor, distinct keys). | Populated via DBMS_STATS or ANALYZE . |
ALL_IND_STATISTICS | Accessible | Optimizer stats for indexes the user can access. | |
USER_IND_STATISTICS | Owned | Stats for user-owned indexes. | Useful before/after stats collection. |
INDEX_STATS | Manual Analysis | Temporary view populated by ANALYZE INDEX ... VALIDATE STRUCTURE . | Used for index structure validation (e.g., height, blocks). |
INDEX_HISTOGRAM | Manual Analysis | Populated by same ANALYZE statement. Shows distribution of leaf rows by block. | Rarely used, but good for detailed analysis or corruption checks. |
USER_OBJECT_USAGE | Monitoring | Shows if an index was used since ALTER INDEX ... MONITORING USAGE . | Essential for deciding whether to drop an index. |
Sample Queries:
-- List indexes on a table
SELECT index_name, index_type FROM USER_INDEXES WHERE table_name = 'EMPLOYEES';
-- Show indexed columns
SELECT index_name, column_name FROM USER_IND_COLUMNS WHERE table_name = 'EMPLOYEES';
-- See if an index was used (after enabling monitoring)
SELECT * FROM USER_OBJECT_USAGE WHERE index_name = 'EMP_EMAIL_IDX';