ALTER TABLE…SPLIT PARTITION

ALTER TABLE ... SPLIT PARTITION 用于将一个范围(RANGE)或列表(LIST)分区拆分为两个新分区,此操作涉及数据重组且不可逆,执行前需规划新分区边界与名称并确认数据已备份。

语法介绍

-- 拆分 RANGE 分区
ALTER TABLE table_name SPLIT PARTITION partition_name
  AT (range_part_value)
  INTO 
  (
    PARTITION new_part1 
      [TABLESPACE tablespace_name],
    PARTITION new_part2 
      [TABLESPACE tablespace_name]
  ); 

-- 拆分 LIST 分区
ALTER TABLE table_name SPLIT PARTITION partition_name
  VALUES (value[, value]...) 
  INTO 
  (
    PARTITION new_part1 
      [TABLESPACE tablespace_name],
    PARTITION new_part2 
      [TABLESPACE tablespace_name]
  );

参数说明

参数名称

参数要求

参数说明

参数示例

table_name

必填

目标分区表的名称。

log_archives

partition_name

必填

要拆分的现有分区的名称。

p_2022

AT (range_part_value)

用于 RANGE 分区拆分

定义 RANGE 分区的拆分点。该值将成为第二个新分区的下边界。

AT (TO_DATE('2022-07-01', 'YYYY-MM-DD'))

VALUES (value[, value]...)

用于 LIST 分区拆分

定义 LIST 分区拆分时,分配给第一个新分区的值列表。

VALUES ('China', 'Singapore')

INTO (...)

必填

指定两个新分区的名称和可选的表空间。new_part1 包含拆分点之前的数据,new_part2 包含拆分点之后的数据。

INTO (PARTITION p_h1, PARTITION p_h2)

TABLESPACE tablespace_name

可选
默认值:表的默认表空间

为新分区指定存储表空间。可为两个新分区指定不同或相同的表空间。

TABLESPACE tbs_archive

注意事项

  • 执行此命令的用户需是该表的所有者,或拥有对该表的 ALTER 权限。

  • SPLIT PARTITION 会获取 AccessExclusiveLock(表级排他锁),阻塞该表的所有 DML 和大部分 DDL 操作。建议在业务低峰期执行,并预留足够的时间窗口。

  • 拆分点的定义必须合法:

    • AT的值必须在被拆分RANGE分区的上下界之间;

    • VALUES的值列表必须是被拆分LIST分区值列表的子集。

使用示例

拆分范围(RANEG)分区

本示例演示如何将一个年度日志分区 p_2022 拆分为两个半年度分区 p_2022_h1 和 p_2022_h2

环境准备

本步骤将创建一个按日志时间范围分区的表 log_archives,并插入跨越拆分点的数据。

-- 创建分区表
CREATE TABLE log_archives (
    log_id   INT NOT NULL,
    log_time DATE NOT NULL
)
PARTITION BY RANGE (log_time) (
    PARTITION p_2022 VALUES LESS THAN (TO_DATE('2023-01-01', 'YYYY-MM-DD'))
);

INSERT INTO log_archives VALUES (1, TO_DATE('2022-01-10', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (2, TO_DATE('2022-02-15', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (3, TO_DATE('2022-03-20', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (4, TO_DATE('2022-05-05', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (5, TO_DATE('2022-06-25', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (6, TO_DATE('2022-07-15', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (7, TO_DATE('2022-08-01', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (8, TO_DATE('2022-10-30', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (9, TO_DATE('2022-11-11', 'YYYY-MM-DD'));
INSERT INTO log_archives VALUES (10, TO_DATE('2022-12-24', 'YYYY-MM-DD'));

前置检查

在执行拆分操作前,确认数据已备份,并检查原始分区的存在性。

-- 重要:执行拆分操作前,请确保已对相关数据进行备份。
SELECT PARTITION_NAME FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'LOG_ARCHIVES';

执行操作

以 2022-07-01 为拆分点,执行 SPLIT PARTITION 命令。

ALTER TABLE log_archives SPLIT PARTITION p_2022 
AT (TO_DATE('2022-07-01', 'YYYY-MM-DD'))
INTO (PARTITION p_2022_h1, PARTITION p_2022_h2);

结果验证

验证原始分区被替换为两个新分区,且数据已正确分布。

-- 结构验证:确认新分区已创建,旧分区已消失
SELECT PARTITION_NAME FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'LOG_ARCHIVES';
-- 查询结果应包含 p_2022_h1, p_2022_h2,不包含 p_2022

-- 数据验证:确认数据按拆分点分布
SELECT COUNT(*) FROM log_archives PARTITION (p_2022_h1); -- 结果应为 5
SELECT COUNT(*) FROM log_archives PARTITION (p_2022_h2); -- 结果应为 5

拆分列表(LIST)分区

本示例演示如何将一个包含多个国家/地区的亚洲销售分区 p_asia,拆分为一个单独的中国分区 p_china 和一个包含其他亚洲国家/地区的分区 p_asia_others

环境准备

本步骤将创建一个按销售地区列表分区的表 sales_by_region

-- 创建分区表
CREATE TABLE sales_by_region (
    sale_id INT,
    country VARCHAR2(20)
)
PARTITION BY LIST (country) (
    PARTITION p_asia VALUES ('China', 'Japan', 'Korea', 'Singapore')
);
INSERT INTO sales_by_region VALUES (101, 'China');
INSERT INTO sales_by_region VALUES (102, 'China');
INSERT INTO sales_by_region VALUES (201, 'Japan');
INSERT INTO sales_by_region VALUES (301, 'Korea');
INSERT INTO sales_by_region VALUES (401, 'Singapore');

前置检查

检查待拆分的 p_asia 分区是否存在及其数据分布。

SELECT PARTITION_NAME, HIGH_VALUE FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'SALES_BY_REGION';
SELECT country, COUNT(*) FROM sales_by_region GROUP BY country;

执行操作

使用 VALUES 子句,将 p_asia 分区中值为 'China' 的数据拆分到新分区 p_china

ALTER TABLE sales_by_region SPLIT PARTITION p_asia
VALUES ('China')
INTO (PARTITION p_china, PARTITION p_asia_others);

结果验证

验证原始分区被替换为两个新分区,它们的值列表已正确分配,并且数据也随之迁移。

-- 结构验证:确认新分区已创建及其值列表
SELECT PARTITION_NAME, HIGH_VALUE FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'SALES_BY_REGION';
-- 查询结果应包含:
-- P_CHINA, 'China'
-- P_ASIA_OTHERS, 'Japan', 'Korea', 'Singapore'

-- 数据验证:确认数据已按新分区分布
SELECT COUNT(*) FROM sales_by_region PARTITION (p_china);       -- 结果应为 2
SELECT COUNT(*) FROM sales_by_region PARTITION (p_asia_others); -- 结果应为 3

常见问题

Q1: 执行时报错 ORA-14080: partition cannot be split
这通常是因为您定义的拆分点无效。对于 RANGE 分区,AT 的值必须在分区上下界之间;对于 LIST 分区,VALUES 的值列表必须是被拆分分区值列表的子集(非空且非全部)。

Q2: 执行时报错 ORA-14078: partition name is already in use
这是因为您在 INTO 子句中指定的新分区名称已经被该表中的其他分区使用,需要更换一个在表内唯一的名称。

Q3: SPLIT PARTITION 操作执行缓慢是什么原因?
此操作涉及物理数据的重组和拷贝,其执行时间与分区数据量成正比,会产生大量I/O。这是一个资源密集型操作,建议在业务低峰期执行。

相关语句