【问题标题】:How to drop multiple interval partitions based on date?如何根据日期删除多个间隔分区?
【发布时间】:2018-06-13 19:47:36
【问题描述】:

我有一个基于每日分区的表。

我可以使用以下查询删除分区

ALTER TABLE MY_TABLE DROP PARTITION FOR(TO_DATE('19-DEC-2017','dd-MON-yyyy'))

如何在 15 天之前删除所有分区(多个分区)?

【问题讨论】:

  • 我理解基于名称的删除。我正在尝试检查是否有任何基于日期范围的替代方案。
  • 您也可以在名称上删除分区。 ALTER TABLE sales DROP PARTITION sales_q1_2008, sales_q2_2008, sales_q3_2008, sales_q4_2008; 。我不认为日期范围内有什么东西
  • @XING - 谢谢

标签: oracle partition


【解决方案1】:

你可以像这样使用 PL/SQL。

DECLARE
    CANNOT_DROP_LAST_PARTITION EXCEPTION;
    PRAGMA EXCEPTION_INIT(CANNOT_DROP_LAST_PARTITION, -14758);

   ts TIMESTAMP;
BEGIN
   FOR aPart IN (SELECT PARTITION_NAME, HIGH_VALUE FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'MY_TABLE') LOOP
      EXECUTE IMMEDIATE 'BEGIN :ret := '||aPart.HIGH_VALUE||'; END;' USING OUT ts;
      IF ts < SYSTIMESTAMP - INTERVAL '15' DAY THEN
      BEGIN
         EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE DROP PARTITION '||aPart.PARTITION_NAME|| ' UPDATE GLOBAL INDEXES';
      EXCEPTION
            WHEN CANNOT_DROP_LAST_PARTITION THEN
                EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE SET INTERVAL ()';
                EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE DROP PARTITION '||aPart.PARTITION_NAME;
                EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE SET INTERVAL( INTERVAL ''1'' DAY )';            
      END;
      END IF;
   END LOOP;
END;

【讨论】:

  • 在 USER_TAB_PARTITIONS 表中,高值是 "TIMESTAMP' 2017-01-01 00:00:00'" 。那么我需要做出哪些改变?
  • 为什么是'BEGIN :ret := '||aPart.HIGH_VALUE||';结尾;'必需的?为什么我们不能直接使用 aPart.HIGH_VALUE?
  • 因为HIGH_VALUE 是数据类型LONG 有很多限制。不能直接使用,IF aPart.HIGH_VALUE &lt; ...FROM USER_TAB_PARTITIONS WHERE HIGH_VALUE &lt; ...会失效。
  • 我把它放在我的回答中。您必须手动删除的最后一个(即最初创建的)分区。删除最后一个分区后,您将不会再次收到错误。
  • 关于“为什么我们不能直接使用aPart.HIGH_VALUE?”,是因为该列在数据库中存储为LONG。因此,它是该时间戳的字符串表示形式,而不是时间戳本身。
【解决方案2】:

对于间隔分区表(您可能根据ORA-14758 异常使用),您可能会从使用partition_extended_name 语法中受益。

您不需要知道分区名称,您可以使用 DATE 来引用分区,例如

alter table INT_PART drop partition for (DATE'2018-09-01')

所以要删除从当天开始的最后 15 个分区,要执行此循环:

declare
 v_sql VARCHAR2(4000);
begin
  for cur in (select  
                trunc(sysdate,'MM') - numtodsinterval(rownum - 1, 'day') my_month
              from dual connect by level <= 15) 
  loop
     v_sql := q'[alter table INT_PART drop partition for (DATE']'||
                 to_char(cur.my_month,'YYYY-MM-DD')||q'[')]';
     execute immediate v_sql;
  end loop;
end;
/

您必须使用 execute immediate 作为 ALTER TABLE 语句中的 DATE 必须是静态的。 生成并执行以下语句:

alter table INT_PART drop partition for (DATE'2018-09-01')
alter table INT_PART drop partition for (DATE'2018-08-31')
....
alter table INT_PART drop partition for (DATE'2018-08-19')
alter table INT_PART drop partition for (DATE'2018-08-18')

除了 ORA-14758 异常(我忽略 - 请参阅下面的注释)之外,您应该处理异常

ORA-02149: 指定的分区不存在

取决于您的业务,这可能应该被忽略 - 因为这一天不存在分区(并且您永远不会使用分区字典元数据引用这一天)。

注意要解决ORA-14758 Last partition in the range section cannot be dropped 异常,您可以使用这个小技巧。

我创建了一个名为P_MINVALUEdummy 分区(没有范围),它在过去扮演间隔开始的角色,因此永远不会被丢弃。

CREATE TABLE int_part
      (
     transaction_date TIMESTAMP not null,
     vc_pad VARCHAR2(100)
      )
     SEGMENT CREATION DEFERRED 
     PARTITION BY RANGE (transaction_date) INTERVAL (NUMTODSINTERVAL(1,'DAY'))
      (
     PARTITION P_MINVALUE  VALUES LESS THAN (TO_DATE('2000-01-01', 'YYYY-MM-DD') ) 
   );

【讨论】:

  • 语法PARTITION FOR (DATE '...')被称为“分区扩展名”
  • @WernfriedDomscheit 感谢您指出这一点。我编辑了链接。奇特的方法是它必须用下划线partition_extended_name 编写才能在Oracle 文档中找到它。
猜你喜欢
  • 1970-01-01
  • 2020-02-23
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多