【问题标题】:Truncate partitions older than 2 months截断超过 2 个月的分区
【发布时间】:2023-10-25 21:09:01
【问题描述】:

如何截断数据超过 2 个月的分区?

例如,我有以下表/分区名称:

select table_name, partition_name from all_tab_partitions where table_name='TABLENAME';

TABLENAME   partitionname1_P30    30
TABLENAME   partitionname2_P60    60 
TABLENAME   partitionname3_P90    90
TABLENAME   partitionname4_P120   120
TABLENAME   partitionname5_P150   150
TABLENAME   partitionname6_P180   180 
TABLENAME   partitionname7_210    210
TABLENAME   partitionname8_P240   240
TABLENAME   partitionname9_P270   270
TABLENAME   partitionname10_P300  300
TABLENAME   partitionname11_P330  330
TABLENAME   partitionname12_P360  360  

表每月分区。如果现在是 9 月,如何截断超过 2 个月的分区?

预计将仅保留 8 月至 9 月 (partitionname8-9) 的记录,而其余的将被截断。

CREATE TABLE dbo1.TABLENAME
( PARTITION_ID NUMBER(4, 0) NOT NULL, 
TABLE_DATE DATE NOT NULL, 
TABLE_TIMESTAMP NUMBER(19, 0) NOT NULL, 
TABLE_BUNDLE_ID VARCHAR2(240 BYTE) NOT NULL, 
TABLE_TYPE NUMBER(8, 0) NOT NULL, 
TABLE_SEVERITY NUMBER(19, 0) NOT NULL,
TABLE_FACILITY NUMBER(19, 0) NOT NULL,
TABLE_HOST VARCHAR2(120 BYTE) NOT NULL,
TABLE_PROCESS VARCHAR2(240 BYTE) NOT NULL,
TABLE_SYSTEM VARCHAR2(240 BYTE) NOT NULL,
TABLE_SESSION_ID VARCHAR2(240 BYTE) NOT NULL,
TABLE_PRINCIPAL VARCHAR2(120 BYTE) NOT NULL,
OBJECT_ID VARCHAR2(120 BYTE),
OBJECT_TYPE VARCHAR2(2 BYTE),
CLIENT_HOST VARCHAR2(120 BYTE),
ACCESS_HOST VARCHAR2(120 BYTE),
SCOPE_ID VARCHAR2(120 BYTE),
STATUS NUMBER(19, 0),
OBJECT_HISTORY NUMBER(19, 0),
TABLE_DETAILS VARCHAR2(4000 BYTE) 
) 
PARTITION BY RANGE (PARTITION_ID) 
(
PARTITION partitionname1_P30 VALUES LESS THAN (30) 
,<repeat partition by 30s up to 360, total of 12 partitions>

【问题讨论】:

  • 你的 DBMS 是什么,好像是 Oracle ...?
  • "表按月分区" - 请确认这意味着范围分区。
  • 对于间隔分区,请参阅解决方案here
  • 你的分区定义很奇怪。据我了解,分区partitionname2_P60 包含从 1 月 30 日到 3 月 1 日的数据(如果是叶年),即它涵盖了三个月——“超过 2 个月”是什么意思?在TABLE_DATE 或虚拟列EXTRACT(MONTH FROM TABLE_DATE) 上创建一个简单的INTERVAL 分区

标签: oracle truncate database-partitioning


【解决方案1】:

你可以这样做:

DECLARE

    CURSOR PartTables IS
    SELECT TABLE_NAME, PARTITION_NAME, HIGH_VALUE
    FROM USER_TAB_PARTITIONS
    WHERE TABLE_NAME = 'TABLENAME';

    highValue TIMESTAMP;

BEGIN
   FOR aTab IN PartTables LOOP
      EXECUTE IMMEDIATE 'BEGIN :ret := '||aTab.HIGH_VALUE||'; END;' USING OUT highValue;
      IF highValue < ADD_MONTHS(SYSDATE, -2) THEN
         EXECUTE IMMEDIATE 'ALTER TABLE TABLENAME TRUNCATE PARTITION '||aTab.PARTITION_NAME||' UPDATE INDEXES';
      END IF;
   END LOOP;
END;

这适用于基于 RANGE 或 INTERVAL 的分区,但是在这种情况下,您的要求相当无用,因为您将永远保留空分区。通常你会删除旧分区,为此只需将TRUNCATE 替换为DROP

如果您的分区基于 LIST,即月份数,则解决方案如下:

DECLARE

    CURSOR PartTables IS
    SELECT TABLE_NAME, PARTITION_NAME, HIGH_VALUE
    FROM USER_TAB_PARTITIONS
    WHERE TABLE_NAME = 'TABLENAME';

    highValue INTEGER;

BEGIN
   FOR aTab IN PartTables LOOP
      EXECUTE IMMEDIATE 'BEGIN :ret := '||aTab.HIGH_VALUE||'; END;' USING OUT highValue;
      IF highValue NOT IN (
            EXTRACT(MONTH FROM SYSDATE),
            EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -1))
         ) 
      THEN
         EXECUTE IMMEDIATE 'ALTER TABLE TABLENAME TRUNCATE PARTITION '||aTab.PARTITION_NAME||' UPDATE INDEXES';
      END IF;
   END LOOP;
END;

根据你奇怪的分区定义,条件是

IF highValue NOT IN (
    30*CEIL(TO_CHAR(SYSDATE, 'fmddd')/30), 
    30*CEIL(TO_CHAR(ADD_MONTHS(SYSDATE, -1), 'fmddd')/30)
    ) 
THEN

但如果您在 12 月 26 日至 31 日之间运行该程序,您可能会遇到问题。

【讨论】:

  • 你能解释一下这个吗?因为我之前没有尝试过使用 TRUNCATE ..
  • 什么意思?您问“截断分区...” - 这是一种解决方案。
  • 我尝试了第一个但它抛出`DECLARE` * ERROR at line 1: ORA-06550: line 1, column 15: PLS-00382: expression is of wrong type PLS-00382: expression is of wrong type ORA-06550: line 1, column 7: PL/SQL: Statement ignored ORA-06512: at line 12
  • 请显示您的表定义,即 CREATE TABLE 语句。你是如何定义分区的?
  • 请查看表定义问题中的更新。我还在几列上添加了表约束