用于为具有 7000 个分区的分区 Table 的日期范围生成语法的脚本

Script to Generate Syntax for Date Range Partitioned Table with 7000 partitions

我必须创建一个日期范围为 20 年的分区 table 范围。每个分区将包含一天的数据。

因此 table 将有大约 7,000 个分区 ( 20*365 )。因此手动创建将非常容易出错。

有没有可以生成所有分区语法的脚本?

这样的事情怎么样?

按范围划分 (date_col) 间隔 (numtoyminterval(1, 'DAY')) (分区P_OLDDATA 值小于 (to_date('2021 年 1 月 1 日','DD-MON-YYYY')) ); 该语句创建一个 table 具有:

< 2021 年 1 月 1 日的锚定分区:这意味着任何早于 2021 年 1 月 1 日的数据都将进入分区 P_OLDDATA。 1 天间隔:这意味着日期为 2021 年 1 月 1 日或之后的任何数据都将进入其自己的日分区。如果这样的分区不存在,Oracle 将自动创建它。

总而言之,您的历史数据将在 1 个分区中,您的新数据将每天分区在其自己的分区中。

依赖名称是糟糕的编码,您的应用程序应该得到修复。

您可以使用简单的查询,而不是使用分区名称来查找每天的行数。

用正确的变量名替换 create_date。

select trunc(created_date), count(*) as num_created
from the_table
group by trunc(created_date);

trunc() 是必需的,因为在 Oracle 中 DATE 列也包含时间部分(或者如果您的 created_date 实际上是时间戳而不是日期)。

这是我不久前写的分区维护例程中的一些代码...应该可以帮助您入门。我过去常常安排它每晚寻找间隔并清理它们

declare
  l_high_value         varchar2(4000);
  l_partition_boundary date;
  l_parname            varchar2(40);
  l_ddl                varchar2(4000);
begin
    for i in ( 
      select a.owner, a.table_name, a.partitioning_type, t.partition_name, t.high_value, a.interval
      from   all_part_tables a, all_tab_partitions t
      where  a.owner = ...
      and    a.partitioning_type = 'RANGE'
      and    a.table_name not like 'BIN$%'
      and    a.interval is not null
      and    t.table_owner = a.owner
      and    t.table_name  = a.table_name
      and    regexp_like(t.partition_name,'^SYS_P[[:digit:]]{1,10}') ) 
    loop  
      begin
        l_high_value := i.high_value;
        execute immediate 'select '||l_high_value||'-1 from dual' into l_partition_boundary;

        if upper(i.interval) like 'NUMTODSINTERVAL%' then
           l_parname := substr(i.table_name,1,21)||'_'||to_char(l_partition_boundary,'YYYYMMDD');
        elsif upper(i.interval) like 'NUMTOYMINTERVAL%MONTH%' then
           l_parname := substr(i.table_name,1,23)||'_'||to_char(l_partition_boundary,'YYYYMM');
        elsif upper(i.interval) like 'NUMTOYMINTERVAL%YEAR%' then
           l_parname := substr(i.table_name,1,25)||'_'||to_char(l_partition_boundary,'YYYY');
        elsif upper(i.interval) like 'INTERVAL''%''DAY%' then
           l_parname := substr(i.table_name,1,21)||'_'||to_char(l_partition_boundary,'YYYYMMDD');
        elsif upper(i.interval) like 'INTERVAL''%''MONTH%' then
           l_parname := substr(i.table_name,1,23)||'_'||to_char(l_partition_boundary,'YYYYMM');
        elsif upper(i.interval) like 'INTERVAL''%''YEAR%' then
           l_parname := substr(i.table_name,1,25)||'_'||to_char(l_partition_boundary,'YYYY');
        else
           l_parname := null;
           dbms_output.put_line ('Cannot determine correct partition name for table ' || i.table_name || ' partition ' || i.partition_name); 
        end if;
      exception
        when others then
           l_parname := null;
           dbms_output.put_line ('Cannot determine correct partition name for table ' || i.table_name || ' partition ' || i.partition_name||' hi='||l_high_value); 
      end;

      if (l_parname is not null) then
        l_ddl := 'alter table '||i.owner||'.'||i.table_name||' rename partition '||i.partition_name||' to '||l_parname;
        dbms_output.put_line(l_ddl);

        begin
          execute immediate l_ddl;
        exception
          when others then
            dbms_output.put_line('Failed with '||sqlerrm);
        end;

      end if;
    end loop;

    --
    -- then we can use those new partition names to do the same for indexes
    --

    for i in ( 
            with tabpar as ( 
             select /*+ materialize */ 
                     a.owner, a.table_name, a.partitioning_type, t.partition_name, t.partition_position
              from   all_part_tables a, all_tab_partitions t
              where  a.owner = ...
              and    a.partitioning_type = 'RANGE'
              and    a.table_name not like 'BIN$%'
              and    a.interval is not null
              and    t.table_owner = a.owner
              and    t.table_name  = a.table_name
            ), indpar as (
             select /*+ materialize */ 
                     ai.owner, ai.table_name, ai.index_name, i.partition_name, i.partition_position
              from   all_part_indexes ai, all_ind_partitions i
              where  ai.owner = ...
              and    ai.partitioning_type = 'RANGE'
              and    ai.table_name not like 'BIN$%'
              and    ai.interval is not null
              and    i.index_owner = ai.owner
              and    ai.index_name  = i.index_name
              and    ai.locality = 'LOCAL'
            )
            select t.owner, t.table_name, t.partition_name new_par_name, i.index_name, i.partition_name
                  from   tabpar t, indpar i
                  where  i.owner = t.owner
                  and    i.table_name = t.table_name
                  and    i.partition_position = t.partition_position
                  and    not regexp_like(t.partition_name,'^SYS_P[[:digit:]]{1,10}')
                  and    regexp_like(i.partition_name,'^SYS_P[[:digit:]]{1,10}')   
    )
    loop  
      l_ddl := 'alter index '||i.owner||'.'||i.index_name||' rename partition '||i.partition_name||' to '||i.new_par_name;
      dbms_output.put_line(l_ddl);
      begin
        execute immediate l_ddl;
      exception
        when others then
          dbms_output.put_line('Failed with '||sqlerrm);
      end;
    end loop;
end;
/

我公司有人对自动分区名称有同样的担忧,所以我们现在使用这个程序。

您可以手动 运行 它或设置计划任务以自动 运行 它。我会建议后者。

如果您喜欢这个答案,请点赞并采纳。


CREATE OR REPLACE PROCEDURE MaintainPartitions IS  EXPRESSION_IS_OF_WRONG_TYPE EXCEPTION;
    PRAGMA EXCEPTION_INIT(EXPRESSION_IS_OF_WRONG_TYPE, -6550);

    CURSOR PartTables IS
    SELECT TABLE_NAME, INTERVAL
    FROM USER_PART_TABLES 
    WHERE PARTITIONING_TYPE = 'RANGE' 
    ORDER BY TABLE_NAME;

    CURSOR TabParts(aTableName VARCHAR2) IS 
    SELECT PARTITION_NAME, HIGH_VALUE
    FROM USER_TAB_PARTITIONS
 WHERE regexp_like(partition_name,'^SYS_P[[:digit:]]{1,10}')  AND
 TABLE_NAME = aTableName AND
table_name not like 'BIN$%'
      and    interval is not null
    ORDER BY PARTITION_POSITION;

    ym INTERVAL YEAR TO MONTH;
    ds INTERVAL DAY TO SECOND;
    newPartName VARCHAR2(30);
    PERIOD TIMESTAMP;

BEGIN

    FOR aTab IN PartTables LOOP 
        BEGIN       
            EXECUTE IMMEDIATE 'BEGIN :ret := '||aTab.INTERVAL||'; END;' USING OUT ds;
            ym := NULL; 
        EXCEPTION 
            WHEN EXPRESSION_IS_OF_WRONG_TYPE THEN
                EXECUTE IMMEDIATE 'BEGIN :ret := '||aTab.INTERVAL||'; END;' USING OUT ym;
                ds := NULL;         
        END;            

        FOR aPart IN TabParts(aTab.TABLE_NAME) LOOP         
            EXECUTE IMMEDIATE 'BEGIN :ret := '||aPart.HIGH_VALUE||'; END;' USING OUT PERIOD;
            IF ds IS NOT NULL THEN
                IF ds >= INTERVAL '7' DAY THEN
                    -- Weekly partition
                    EXECUTE IMMEDIATE 'BEGIN :ret := TO_CHAR('||aPart.HIGH_VALUE||' - :int, :fmt); END;' USING OUT newPartName, INTERVAL '1' DAY, '"P_"IYYY"W"IW';
                ELSE
                    -- Daily partition
                    EXECUTE IMMEDIATE 'BEGIN :ret := TO_CHAR('||aPart.HIGH_VALUE||' - :int, :fmt); END;' USING OUT newPartName, INTERVAL '1' DAY, '"P_"YYYYMMDD';
                END IF;
            ELSE
                IF ym = INTERVAL '3' MONTH THEN
                    -- Quarterly partition 
                    EXECUTE IMMEDIATE 'BEGIN :ret := TO_CHAR('||aPart.HIGH_VALUE||' - :int, :fmt); END;' USING OUT newPartName, INTERVAL '1' DAY, '"P_"YYYY"Q"Q';
                ELSE
                    -- Monthly partition
                    EXECUTE IMMEDIATE 'BEGIN :ret := TO_CHAR('||aPart.HIGH_VALUE||' - :int, :fmt); END;' USING OUT newPartName, INTERVAL '1' DAY, '"P_"YYYYMM';
                END IF;
            END IF;

            IF newPartName <> aPart.PARTITION_NAME THEN
                EXECUTE IMMEDIATE 'ALTER TABLE '||aTab.TABLE_NAME||' RENAME PARTITION '||aPart.PARTITION_NAME||' TO '||newPartName;
            END IF;             
        END LOOP;
    END LOOP;

END MaintainPartitions;

EXEC MaintainPartitions