我可以从分区查询数据,但不能删除分区 (ORA-14006)?

I can query data from partitions, but not drop the partitions (ORA-14006)?

我有一个名为 schema.exampletab 的 table,它有分区,其中分区名称的格式为 Pxxxxxx,其中 x 是分区周期(年和月 YYYYMM),然后有一个本地PK。我想删除一个分区,但我收到错误消息 ORA-14006:尝试删除它时分区名称无效,但是当我尝试简单地从分区查询数据时,它 selects 并显示来自有问题的分区。请注意,我使用的调度程序使用 YYYYMMDD 将日期用作参数,因此我通常使用子字符串。

例如我可以使用以下语句查询数据:

SELECT *
FROM   schema.exampletab
PARTITION (P202110);

这returns 20行数据在我的table。 然后我尝试使用以下语句删除分区:

ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
 UPDATE INDEXES;

但是,这会导致出现 ORA-14006 错误消息。为了控制分区的存在,我试图查找架构 table 和分区存在于 all_tab_partitions 中,其中记录了我使用的数据库中的所有分区。

select partition_name from all_tab_partitions 
where table_owner = 'schema' and table_name = 'exampletab' and 
substr(partition_name,2,7) = substr('20211022',1,6);

这个returns查询结果中的分区名P202110

我希望我可以像这样将删除分区与子查询一起使用:

ALTER TABLE schema.exampletab DROP PARTITION select partition_name from all_tab_partitions 
where table_owner = 'schema' and table_name = 'exampletab' and 
substr(partition_name,2,7) = substr('20211022',1,6);

但是,这仍然会导致 ORA-14006 错误。我尝试将分区名称写为 'P202110' 和 P202110 而不是括号,但没有成功。

如何编写删除分区语句以删除分区而不是给出 ORA-14006 错误?

这是我必须经常做的事情,所以很高兴知道如何正确 select 和删除分区,或截断它们等。此外,我使用一个运行 select 的调度程序=38=] 以指定的时间间隔查询,所以我必须将日期指定为参数,这意味着在我的示例代码中 YYYYMM 是一个被解析的参数值,所以我需要将 P 与这个输出的参数值连接起来,因为如果我只输入 P202110 它实际上掉了它。

您不能在分区名称中使用“表达式”

这是错误的:

ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
 UPDATE INDEXES;

这是正确的:

ALTER TABLE schema.exampletab DROP PARTITION P20211011
 UPDATE INDEXES;

您可以使用此代码动态删除分区。只需更新 header 中的 C_OWNER ,C_TABLE_NAME , C_PARTITION_TEMPLATE 。请注意 sql 命令是动态构建的,但是当它准备就绪时,分区名称已完全解析。

declare
    C_OWNER varchar2(128) := 'MYOWNER';
    C_TABLE_NAME varchar2(128) := 'MYTABLE';
    C_PARTITION_TEMPLATE varchar2(128) := '2011';
    cursor part_cur is 
        select * 
        from all_tab_partitions
        where table_owner=C_OWNER and
            table_name = C_TABLE_NAME and
            partition_name like '%'||C_PARTITION_TEMPLATE||'%'
        order by partition_position;
BEGIN
    for part_rec in part_cur loop
        execute immediate 'ALTER TABLE "'||part_rec.table_owner||'"."'||part_rec.table_name||'"'||
            ' DROP PARTITION ("'||part_rec.partition_name||'") UPDATE INDEXES';
    end loop;
END;
/        

您有两个选择,请考虑到您正在使用 sysdate 来了解您应该删除哪个分区。

选项 1 -> 动态 SQL 并且结果必须在查询之外执行

select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; ' 
from 
dba_Tab_partitions
where table_owner = 'your_schema' 
and table_name = 'your_table' 
and partition_name = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;  ;

此查询为您提供命令输出,但您需要执行它

alter table yourschema.yourtable drop partition P202110 update indexes; 

我自己环境中的示例

SQL> select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; '
from
dba_Tab_partitions
where table_owner = 'FDM_DATA'
and table_name = 'FDM_DIM_CUSTOMER'
and partition_name = 'P_'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;

'ALTERTABLE'||TABLE_OWNER||'.'||TABLE_NAME||'DROPPARTITION'||PARTITION_NAME||'UP
--------------------------------------------------------------------------------
 alter table FDM_DATA.FDM_DIM_CUSTOMER drop partition P_202110 update indexes ;

选项 2 -> PLSQL

更好的选择是使用 PLSQL。一个小例子,当你只想删除一个基于当前 sysdate 的分区时。您可以扩展/修改此代码以涵盖任何类型的时间范围。

declare
    v_owner          varchar2(128) := 'YOUR_SCHEMA';
    v_table_name     varchar2(128) := 'YOUR_TABLE';
    v_partition_name varchar2(128);
begin 
  select partition_name into v_partition_name from all_tab_partitions
            where table_owner = v_owner        and
            table_name        = v_table_name   and
            partition_name    = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;
   execute immediate 'alter table '||v_owner||'.'||v_table_name||' drop partition '||v_partition_name||' update indexes' ;
exception 
when no_data_found then null; -- if there is no partition, nothing to do and no error is raised
when others then raise;
end;
/