You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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列,这会导致自引用错误,无法正确获取目标分区,但这不是当前报错的直接原因。

解决方案
  1. 修正索引分区不可用的语法
    将设置索引分区不可用的语句修改为正确的语法:
ALTER INDEX <ix_name> MODIFY PARTITION <current_partition> UNUSABLE;

对应脚本中的执行语句应改为:

EXECUTE IMMEDIATE 'ALTER INDEX ' || ix_name || ' MODIFY PARTITION ' || current_partition || ' UNUSABLE';
  1. 修正分区名查询的逻辑问题
    原查询中动态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的自引用问题。

  1. 可选优化:创建索引时直接指定不可用
    如果创建索引后立刻要将分区设为不可用,可以在创建索引时直接指定所有分区不可用,后续再单独维护目标分区:
CREATE INDEX <ix_name> ON <t_name>(<col_name>) LOCAL UNUSABLE;

这样创建的本地索引所有分区默认都是不可用状态,无需再单独执行修改操作,能减少一步操作。


内容的提问来源于stack exchange,提问作者M_Gh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:24:54