Oracle 19c本地索引分区设为UNUSABLE时遇ORA-14048错误排查
问题描述
在Oracle Database 19c环境中使用分区表,计划在插入数据前将表上的本地索引设为不可用,插入完成后重建索引。编写了PL/SQL脚本用于检查索引:不存在则创建并设为不可用,已存在则直接设为不可用,但运行时收到错误:
Error marking index partition TABLE_TEST_id_indx: ORA-14048: a partition maintenance operation may not be combined with other operations
脚本代码如下:
SET SERVEROUTPUT ON; DECLARE current_date DATE := TO_DATE('2022-12-01', 'YYYY-MM-DD'); -- Example value for current_date current_partition VARCHAR2(50); t_name VARCHAR2(100) := UPPER('table_test'); col_name VARCHAR2(100); ix_name VARCHAR2(150); -- Constructed index name list_index SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST('id'); -- List of columns for indexes index_exists INTEGER; BEGIN DBMS_OUTPUT.enable(); DBMS_OUTPUT.PUT_LINE('Starting the process...'); DBMS_OUTPUT.PUT_LINE('List count: ' || list_index.COUNT); -- Fetch the partition name for the specified current_date SELECT partition_name INTO current_partition FROM all_tab_partitions WHERE table_name = t_name AND TO_DATE(TRIM('''' FROM REGEXP_SUBSTR( EXTRACTVALUE( DBMS_XMLGEN.GETXMLTYPE( 'SELECT high_value FROM all_tab_partitions WHERE table_name=''' || t_name || ''' AND partition_name=''' || partition_name || '''' ), '//text()' ), '''.*?''' )), 'YYYY-MM-DD HH24:MI:SS') = TO_DATE('14010910', 'YYYYMMDD', 'nls_calendar=persian') + 1; DBMS_OUTPUT.PUT_LINE('Current Partition: ' || current_partition); -- Iterate through the list of columns to construct and check index names FOR i IN 1 .. list_index.COUNT LOOP col_name := list_index(i); ix_name := t_name || '_' || col_name || '_indx'; DBMS_OUTPUT.PUT_LINE('Processing index: ' || ix_name); BEGIN -- Step 1: Check if the index exists SELECT COUNT(*) INTO index_exists FROM user_indexes WHERE index_name = UPPER(ix_name) AND table_name = t_name; IF index_exists = 0 THEN -- Create the index if it does not exist DBMS_OUTPUT.PUT_LINE('Creating index: ' || ix_name); EXECUTE IMMEDIATE 'CREATE INDEX ' || ix_name || ' ON ' || t_name || '(' || col_name || ') LOCAL'; DBMS_OUTPUT.PUT_LINE('Index ' || ix_name || ' created.'); END IF; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error creating index ' || ix_name || ': ' || SQLERRM); END; -- Step 2: Mark the local index partition as unusable BEGIN DBMS_OUTPUT.PUT_LINE('Marking index partition as UNUSABLE: ' || ix_name || ', Partition: ' || current_partition); EXECUTE IMMEDIATE 'ALTER INDEX ' || ix_name || ' UNUSABLE LOCAL PARTITION ' || current_partition; DBMS_OUTPUT.PUT_LINE('Index ' || ix_name || ' partition ' || current_partition || ' marked as UNUSABLE.'); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error marking index partition ' || ix_name || ': ' || SQLERRM); END; END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No partition found for the specified current_date.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM); END; /
问题原因
报错ORA-14048的直接原因是修改索引分区状态的语法错误:
你使用了错误的ALTER INDEX语法来设置单个本地索引分区不可用,错误语句为:
ALTER INDEX <ix_name> UNUSABLE LOCAL PARTITION <current_partition>
Oracle不允许将UNUSABLE、LOCAL和PARTITION这几个关键字这样组合使用,这种写法属于非法的分区维护操作组合,触发了ORA-14048错误。
另外,脚本中获取分区名的查询存在逻辑问题:在动态SQL里引用了partition_name列,这会导致自引用错误,无法正确获取目标分区,但这不是当前报错的直接原因。
解决方案
- 修正索引分区不可用的语法
将设置索引分区不可用的语句修改为正确的语法:
ALTER INDEX <ix_name> MODIFY PARTITION <current_partition> UNUSABLE;
对应脚本中的执行语句应改为:
EXECUTE IMMEDIATE 'ALTER INDEX ' || ix_name || ' MODIFY PARTITION ' || current_partition || ' UNUSABLE';
- 修正分区名查询的逻辑问题
原查询中动态SQL引用partition_name会导致错误,建议直接解析当前行的high_value,无需嵌套动态SQL,修改后的分区查询代码如下:
SELECT partition_name INTO current_partition FROM all_tab_partitions WHERE table_name = t_name AND TO_DATE( TRIM('''' FROM REGEXP_SUBSTR(high_value, '''.*?''')), 'YYYY-MM-DD HH24:MI:SS' ) = TO_DATE('14010910', 'YYYYMMDD', 'nls_calendar=persian') + 1;
这样可以直接解析当前行的high_value,避免嵌套动态SQL的自引用问题。
- 可选优化:创建索引时直接指定不可用
如果创建索引后立刻要将分区设为不可用,可以在创建索引时直接指定所有分区不可用,后续再单独维护目标分区:
CREATE INDEX <ix_name> ON <t_name>(<col_name>) LOCAL UNUSABLE;
这样创建的本地索引所有分区默认都是不可用状态,无需再单独执行修改操作,能减少一步操作。
内容的提问来源于stack exchange,提问作者M_Gh
相关产品推荐
相关产品推荐

