CREATE TABLE table_nlssort
(
id NUMBER(2,0) NOT NULL
, desc1 NVARCHAR2(2) NOT NULL
);
INSERT INTO table_nlssort (id, desc1) VALUES (1, 'aé'); INSERT INTO table_nlssort (id, desc1) VALUES (2, 'ae'); INSERT INTO table_nlssort (id, desc1) VALUES (3, 'af'); INSERT INTO table_nlssort (id, desc1) VALUES (4, 'ad'); INSERT INTO table_nlssort (id, desc1) VALUES (5, 'aÉ'); INSERT INTO table_nlssort (id, desc1) VALUES (6, 'aE'); INSERT INTO table_nlssort (id, desc1) VALUES (7, 'aF'); INSERT INTO table_nlssort (id, desc1) VALUES (8, 'aD'); COMMIT;
ALTER SESSION SET NLS_SORT = BINARY; ALTER SESSION SET NLS_COMP = LINGUISTIC;Index Creation and Statistics:
CREATE INDEX idx_table_nlssort_desc1 ON table_nlssort ( desc1 ); ANALYZE INDEX idx_table_nlssort_desc1 COMPUTE STATISTICS;Query Execution with Regular Index:
SELECT * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Access Predicates | +-------------------------+----------------+-------------------+ | TABLE_NLSSORT | BY INDEX ROWID | | | IDX_TABLE_NLSSORT_DESC1 | RANGE SCAN | DESC1 = U'a\00C9' |The execution plan shows that Oracle efficiently uses the index with a RANGE SCAN operation, which is optimal for this type of query.
DROP INDEX idx_table_nlssort_desc1; CREATE INDEX idx_table_nlssort_desc1 ON table_nlssort ( NLSSORT(desc1,'nls_sort=''BINARY_AI''') ); ANALYZE INDEX idx_table_nlssort_desc1 COMPUTE STATISTICS;
SELECT * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Filter Predicates | +---------------+---------+-------------------+ | TABLE_NLSSORT | FULL | DESC1 = U'a\00C9' |When the session NLS_SORT setting doesn't match the function-based index definition, Oracle cannot use the index and resorts to a full table scan. This demonstrates the importance of aligning session parameters with index definitions.
SELECT /*+ index(table_nlssort idx_table_nlssort_desc1) */ * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Filter Predicates | | ------------------------+----------------+-------------------+ | TABLE_NLSSORT | BY INDEX ROWID | DESC1 = U'a\00C9' | | IDX_TABLE_NLSSORT_DESC1 | FULL SCAN | |While it's possible to force index usage with hints, this results in a FULL SCAN of the index rather than an efficient RANGE SCAN, which is typically less performant for selective queries.
ALTER SESSION SET NLS_SORT = BINARY_AI; ALTER SESSION SET NLS_COMP = LINGUISTIC;Regular Index Limitation:
DROP INDEX idx_table_nlssort_desc1; CREATE INDEX idx_table_nlssort_desc1 ON table_nlssort ( desc1 ); ANALYZE INDEX idx_table_nlssort_desc1 COMPUTE STATISTICS;
SELECT * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Filter Predicates |
+---------------+---------+----------------------------------------------------------------+
| TABLE_NLSSORT | FULL | NLSSORT(DESC1,'nls_sort=''BINARY_AI''')=HEXTORAW('0061006500') |
Oracle automatically applies the NLSSORT function to the column in the WHERE clause, but since the index is on the raw column value, it cannot be used effectively.
The HEXTORAW value represents the normalized sort key for the comparison.SELECT /*+ index(table_nlssort idx_table_nlssort_desc1) */ * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Filter Predicates |
+-------------------------+----------------+----------------------------------------------------------------+
| TABLE_NLSSORT | BY INDEX ROWID | |
| IDX_TABLE_NLSSORT_DESC1 | FULL SCAN | NLSSORT(DESC1,'nls_sort=''BINARY_AI''')=HEXTORAW('0061006500') |
Even when forced, the index requires a full scan because the transformation must be applied to every indexed value during the scan.DROP INDEX idx_table_nlssort_desc1; CREATE INDEX idx_table_nlssort_desc1 ON table_nlssort ( NLSSORT(desc1,'nls_sort=''BINARY_AI''') ); ANALYZE INDEX idx_table_nlssort_desc1 COMPUTE STATISTICS;
SELECT * FROM table_nlssort WHERE desc1 = 'aÉ';Execution Plan:
| OBJECT_NAME | OPTIONS | Access Predicates |
+-------------------------+----------------+----------------------------------------------------------------+
| TABLE_NLSSORT | BY INDEX ROWID | |
| IDX_TABLE_NLSSORT_DESC1 | RANGE SCAN | NLSSORT(DESC1,'nls_sort=''BINARY_AI''')=HEXTORAW('0061006500') |
With the properly configured function-based index, Oracle can efficiently perform a RANGE SCAN operation, providing optimal query performance.
The index stores the pre-computed NLSSORT values, eliminating the need for runtime transformations.
CREATE UNIQUE INDEX idx_table_nlssort_desc1 ON table_nlssort (NLSSORT(desc1,'NLS_SORT=BINARY_AI'));Query result:
Error starting at line 1 in command: CREATE UNIQUE INDEX IDX_TABLE_NLSSORT_DESC1 ON TABLE_NLSSORT (NLSSORT(DESC1,'NLS_SORT=BINARY_AI')) Error at Command Line:1 Column:69 Error report: SQL Error: ORA-00904: "DESC1": invalid identifier. *Cause: *Action:This error occurs because the NLSSORT function generates identical sort keys for values that are considered equivalent under the specified collation (such as 'aé' and 'ae' with BINARY_AI). Since multiple rows would produce the same index key, the unique constraint cannot be enforced.