删除大量数据 - 以编程方式截断分区

时间:2016-11-03 12:42:42

标签: oracle

如本question所述,我有以下表格:

create table prova_log(
       id_dispositive       number,
       type                 number,
       date_verification    date,
       status               number
)
partition by range (date_verification) interval (numtoyminterval(1,'MONTH'))
subpartition by list (type)  
subpartition TEMPLATE (
    SUBPARTITION type1 VALUES (1),
    SUBPARTITION type2 VALUES (2),
    SUBPARTITION type3 VALUES (3),
    SUBPARTITION type4 VALUES (4)
)                          
(
   partition p0816 values less than (to_date('01/09/2016','dd/mm/yyyy'))
);

如您所见,我将第一个分区命名为 p0816 。但下一个分区将以随机名称生成吗?

我需要在oracle工作中以编程方式清除表。 我正在考虑 - 因为我的方案是处理1B行/月 - 来删除分区。但是我该怎么做呢?如何按日期范围查找表分区?

我想做类似的事情:

alter table prova_log drop partition XPTO

" XPTO"将是一个随机分区名称,但按日期范围选择 - 比如说3个月之前。换句话说,删除2016/08/01之前的所有内容

3 个答案:

答案 0 :(得分:1)

我像这样使用脚本smt(将表名更改为你的)

declare
  l_limit_date date := sysdate;
begin
  for c in (select table_name,
                   partition_name,
                   interval,
                   high_value_in_date_format
              from (select table_name,
                           partition_name,
                           interval,
                           to_date(trim('''' from regexp_substr(extractvalue(dbms_xmlgen.
                                                                   getxmltype('select high_value from user_tab_partitions where table_name=''' ||
                                                                              table_name ||
                                                                              ''' and partition_name = ''' ||
                                                                              partition_name || ''''),
                                                                   '//text()'),
                                                      '''.*?''')),
                                   'syyyy-mm-dd hh24:mi:ss') high_value_in_date_format
                      from user_tab_partitions
                     where table_name = 'TEST_LOG')
             where high_value_in_date_format <= l_limit_date
             order by high_value_in_date_format) 
  loop
    if (c.interval = 'NO') then
      execute immediate 'alter table TEST_LOG truncate partition ' || c.partition_name;
    else
      execute immediate 'alter table TEST_LOG drop partition ' ||  c.partition_name;
    end if;
  end loop;
end;
/

答案 1 :(得分:0)

在Oracle中,您有两种方法来处理分区,它们是等效的。

ALTER TABLE prova_log DROP PARTITION p0816;

ALTER TABLE prova_log DROP PARTITION FOR ( to_date('01/09/2016','dd/mm/yyyy') );

如果您想解决SUBPARTITION,语法将如下:

ALTER TABLE prova_log TRUNCATE SUBPARTITION FOR ( to_date('01/09/2016','dd/mm/yyyy'), 2 );

为了删除(或截断)分区,我有这个通用的PL / SQL包。最棘手的部分是将HIGH_VALUE LONG数据类型转换为可用值。

FUNCTION DailyPartition(tableName IN VARCHAR2) RETURN BOOLEAN IS    
    EXPRESSION_IS_OF_WRONG_TYPE EXCEPTION;
    PRAGMA EXCEPTION_INIT(EXPRESSION_IS_OF_WRONG_TYPE, -6550);    

    ds INTERVAL DAY TO SECOND;
    ym INTERVAL YEAR TO MONTH;
    str VARCHAR2(1000);     

BEGIN    
    SELECT INTERVAL INTO str FROM USER_PART_TABLES  WHERE TABLE_NAME = tableName;    
    EXECUTE IMMEDIATE 'BEGIN :ret := '||str||'; END;' USING OUT ym;
    RETURN FALSE;    
EXCEPTION 
    WHEN EXPRESSION_IS_OF_WRONG_TYPE THEN
        EXECUTE IMMEDIATE 'BEGIN :ret := '||str||'; END;' USING OUT ds;
        RETURN TRUE;    
END DailyPartition;


PROCEDURE DropPartition(tableName IN VARCHAR2, ts IN TIMESTAMP) IS    
    PARTITION_DOES_NOT_EXIST EXCEPTION;
    PRAGMA EXCEPTION_INIT(PARTITION_DOES_NOT_EXIST, -2149);
    sqlstr VARCHAR2(1000);    

BEGIN    
    sqlstr := 'ALTER TABLE '||tableName||' DROP PARTITION FOR (TIMESTAMP '''||TO_CHAR(ts, 'yyyy-mm-dd hh24:mi:ss')||''') UPDATE GLOBAL INDEXES';
    EXECUTE IMMEDIATE sqlstr;    
EXCEPTION 
    WHEN PARTITION_DOES_NOT_EXIST THEN
        NULL;
END DropPartition;



PROCEDURE CleanupPartitions IS
    sqlstr VARCHAR2(10000);
    ts TIMESTAMP;
    tableName VARCHAR2(30) := 'PROVA_LOG';

    CURSOR TabPartitions IS
    SELECT TABLE_NAME, PARTITION_NAME, HIGH_VALUE
    FROM USER_TAB_PARTITIONS 
    WHERE TABLE_NAME = tableName
        AND PARTITION_NAME <> 'P0816' -- Inital partition cannot be dropped
    ORDER BY 1,2;

BEGIN

    ts := ADD_MONTHS(TRUNC(LOCALTIMESTAMP) - INTERVAL '1' DAY, -6);

    IF DailyPartition(tableName) THEN
        DropPartition(tableName, ts);
        IF EXTRACT(DAY FROM ts) >= 30 THEN
            DropPartition(tab, ts - INTERVAL '1' DAY);
        END IF;
        IF TO_CHAR(ts, 'MM-DD') = '08-31' THEN 
            -- Ensure proper cleanup for February 
            DropPartition(tableName, ts - INTERVAL '2' DAY);
            DropPartition(tableName, ts - INTERVAL '3' DAY);
        END IF;
    ELSE
        DropPartition(tableName, ts);
    END IF;


END CleanupPartitions;

保留时间为6个月。根据您的需要制作它应该没有问题。您甚至可以多次调用过程CleanupPartitions;。通常,它将每月或每天由调度程序作业执行一次。

该软件包适用于每日每月分区。

答案 2 :(得分:0)

无需使用随机名称,根据日期使用可预测的分区名称。

来自文档的示例,命名分区(Maintaining Partitions):

ALTER TABLE sales ADD 
  PARTITION sales_q1_2007 VALUES LESS THAN (TO_DATE('01-APR-2007','dd-MON-yyyy')),
  PARTITION sales_q2_2007 VALUES LESS THAN (TO_DATE('01-JUL-2007','dd-MON-yyyy')),
  PARTITION sales_q3_2007 VALUES LESS THAN (TO_DATE('01-OCT-2007','dd-MON-yyyy')),
  PARTITION sales_q4_2007 VALUES LESS THAN (TO_DATE('01-JAN-2008','dd-MON-yyyy'))
;

此外,如果您始终删除尾部分区,则不一定需要事先知道名称。您可以从数据字典(ALL_TAB_PARTITIONSALL_PART_TABLES等)查询尾部分区。

我会使用易于记忆和编码的命名方案。使用月份和年份,如果您需要更多粒度,一周或一个月内的某个序号,那么对具有LIKE子句的字典的查询应该可以满足您的需求。