Skip to Main Content
  • Questions
  • Error while creating function based index on Oracle 19c with MAX_STRING_SIZE=EXTENDED

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Alpesh.

Asked: February 15, 2022 - 10:23 pm UTC

Last updated: February 17, 2022 - 2:58 am UTC

Version: 19.8.0.0.0

Viewed 1000+ times

You Asked

Hi Tom,

Greetings!!!

I am facing an issue in creating Function based index due to change in max_string_size parameter.
Recently the max_string_size was set to EXTENDED which increased the max size of varchar2 to 32767 Bytes from 4000bytes.
When creating index using the function which returns varchar2 values I am seeing error "ORA-01450: maximum key length (6398) exceeded".

Is there any way to solve the issue and still use function for creating index?

Below are the simplified version of the my code...


show parameter max_string_size

NAME            TYPE   VALUE    
--------------- ------ -------- 
max_string_size string EXTENDED 



CREATE OR REPLACE FUNCTION SSF_GET_FORMATTED_NAME(
    p_first_nm       IN VARCHAR2,
    p_last_nm        IN VARCHAR2 
) RETURN VARCHAR2
    DETERMINISTIC
IS
BEGIN
    RETURN UPPER(regexp_replace( p_first_nm, '[^[:alnum:]]') || '~' || regexp_replace(p_last_nm, '[^[:alnum:]]'));
END;
/


CREATE TABLE TEST_VIRTUAL_COL (
    FIRST_NM VARCHAR2(25 CHAR),
    LAST_NM  VARCHAR2(30 CHAR) 
);

CREATE INDEX FORMATTED_NAME_INDEX ON TEST_VIRTUAL_COL(SSF_GET_FORMATTED_NAME(FIRST_NM, LAST_NM));


Error starting at line : 11 in command -
CREATE INDEX FORMATTED_NAME_INDEX ON TEST_VIRTUAL_COL(FORMATTED_NAME)
Error report -
ORA-01450: maximum key length (6398) exceeded
01450. 00000 -  "maximum key length (%s) exceeded"
*Cause:    
*Action:


and Chris said...

The indexed values must be able to fit in a block. As you've enabled extended data types, the function could return strings up to 32,767 bytes long. 4x more than the default blocksize (8Kb)!

You can get around this by casting the function output. I'd do this in a virtual column, then indexing that:
create table test_virtual_col (
  first_nm       varchar2(25 char),
  last_nm        varchar2(30 char),
  formatted_name varchar2(100 char) as (
    cast ( 
      ssf_get_formatted_name(first_nm, last_nm) 
        as varchar2(100 char) 
    )
  )
);

create index formatted_name_index 
  on test_virtual_col (
    formatted_name
  );


Queries against the virtual column can then use this index:

insert into test_virtual_col 
  values ( 'Chris', 'Saxon', default );
  
set serveroutput off
select * from test_virtual_col
where  formatted_name = 'CHRIS~SAXON';
select * 
from   dbms_xplan.display_cursor( format => 'BASIC' );

--------------------------------------------------------------------
| Id  | Operation                           | Name                 |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                      |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TEST_VIRTUAL_COL     |
|   2 |   INDEX RANGE SCAN                  | FORMATTED_NAME_INDEX |
--------------------------------------------------------------------


This is easier than calling the function and avoids mismatch issues. You can use cast ( fn as ... ) in the index itself, but then all queries must have an identical cast expression to use the index. If you write one with a different length, the optimizer is unable to use it!

For other ways to solve the ORA-01450 error, see
https://blogs.oracle.com/sql/post/how-to-fix-ora-01450-maximum-key-length-6398-exceeded-errors

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Thank you

Alpesh B, February 16, 2022 - 9:27 am UTC

Awesome..thank you very much.
Connor McDonald
February 17, 2022 - 2:58 am UTC

glad we could help

More to Explore

Design

New to good database design? Check out Chris Saxon's full fundamentals class.