How to Partition a Non-partitioned Table [ID 1070693.6]

本文介绍将非分区表转换为分区表的四种方法:导出/导入法、子查询插入法、分区交换法及使用DBMS_REDEFINITION法,并提供具体步骤。

How to Partition a Non-partitioned Table [ID 1070693.6]


Modified03-MAR-2010TypeBULLETINStatusPUBLISHED

PURPOSE

You have a table that is not partitioned that you would like to make into a partitioned table.This article describes four possible methods for partitioning a non-partitioned table.

These steps can also be used to change other partitioning characteristics such as adding subpartitioning to a partitioned table.

SCOPE & APPLICATION

Users needing to partition a non-partitioned table.

RELATED DOCUMENTS

Note:72332.1DIAGNOSING ORA-14097 ON ALTER TABLE EXCHANGE PARTITION

Note:105317.1Section "VII.7 Partition exchanges

Note:472449.1How To Partition Existing Table Using DBMS_Redefinition

You can partition a non-partitioned table in one of four ways:

A)Export/import method

B)Insert with a subquery method

C)Partition exchange method

D)DBMS_REDEFINITION

Either of these four methods will create a partitioned table from an existing non-partitioned table.

A. Export/import method

--------------------

1)Export your table:

exp usr/pswd tables=numbers file=exp.dmp

2)Drop the table:

drop table numbers;

3)Recreate the table with partitions:

create table numbers (qty number(3), name varchar2(15))

partition by range (qty)

(partition p1 values less than (501),

partition p2 values less than (maxvalue));

4)Import the table with ignore=y:

imp usr/pswd file=exp.dmp ignore=y

The ignore=y causes the import to skip the table creation and

continues to load all rows.

B. Insert with a subquery method

-----------------------------

1)Create a partitioned table:

create table partbl (qty number(3), name varchar2(15))

partition by range (qty)

(partition p1 values less than (501),

partition p2 values less than (maxvalue));

2)Insert into the partitioned table with a subquery from the

non-partitioned table:

insert into partbl (qty, name)

select * from origtbl;

3)If you want the partitioned table to have the same name as the

original table, then drop the original table and rename the

new table:

drop table origtbl;

alter table partbl rename to origtbl;

C. Partition Exchange method

-------------------------

ALTER TABLE EXCHANGE PARTITION can be used to convert a partition (or

subpartition) into a non-partitioned table and a non-partitioned table into a

partition (or subpartition) of a partitioned table by exchanging their data

and index segments.

1) Create table dummy_t as select with the required partitions

2) Alter table EXCHANGE partition partition_name

with table non-partition_table;

Example

-------

SQL> CREATE TABLE p_emp

2(sal NUMBER(7,2))

3PARTITION BY RANGE(sal)

4(partition emp_p1 VALUES LESS THAN (2000),

5partition emp_p2 VALUES LESS THAN (4000));

Table created.

SQL> SELECT * FROM emp;

EMPNO ENAMEJOBMGR HIREDATESAL

--------- ---------- --------- --------- --------- ---------

7369 SMITHCLERK7902 17-DEC-80800

7499 ALLENSALESMAN7698 20-FEB-811600

7521 WARDSALESMAN7698 22-FEB-811250

7566 JONESMANAGER7839 02-APR-812975

7654 MARTINSALESMAN7698 28-SEP-811250

7698 BLAKEMANAGER7839 01-MAY-812850

7782 CLARKMANAGER7839 09-JUN-812450

7788 SCOTTANALYST7566 19-APR-873000

7839 KINGPRESIDENT17-NOV-815000

7844 TURNERSALESMAN7698 08-SEP-811500

7876 ADAMSCLERK7788 23-MAY-871100

7900 JAMESCLERK7698 03-DEC-81950

7902 FORDANALYST7566 03-DEC-813000

7934 MILLERCLERK7782 23-JAN-821300

14 rows selected.

SQL> CREATE TABLE dummy_y as SELECT sal

FROM emp WHEREsal<2000;

Table created.

SQL> CREATE TABLE dummy_z as SELECT sal FROM emp WHERE sal

BETWEEN 2000 AND 3999;

Table created.

SQL> alter table p_emp exchange partition emp_p1

with table dummy_y;

Table altered.

SQL> alter table p_emp exchange partition emp_p2

with table dummy_z;

Table altered.

D. DBMS_REDEFINITION

-----------------

See Note 472449.1 "How To Partition Existing Table Using DBMS_Redefinition" for detailed instructions.

在线重定义参考:

http://blog.youkuaiyun.com/xujinyang/article/details/6831289

------------------------------------------------------------------------------

<think> 我们面对两个核心错误: 1. 执行 `CREATE TABLE ... PARTITION OF ...` 时报错 `syntax error at or near "PARTITION OF"` —— 说明高斯数据库不支持PostgreSQL风格的分区表创建语法。 2. 执行 `ALTER TABLE ... ADD PARTITION ...` 时报错 `can not add partition against NON-PARTITIONED table` —— 说明目标表 `event_log_alarm202601` 不是一个分区主表(即没有定义分区)。 因此,正确的解决步骤是: 步骤1:确认目标表是否已经定义为分区主表 步骤2:如果目标表不是分区主表,则需要先将普通表转换为分区主表(或者重新创建为分区主表) 步骤3:然后才能添加分区 具体操作: 1. 检查表结构: 我们需要确认 `evo_event.event_log_alarm202601` 表是否已经定义了分区(即创建时使用了 `PARTITION BY`)。 可以通过查询系统表来确认: ```sql SELECT parttype FROM pg_partition WHERE relname = 'event_log_alarm202601' AND parentid IS NULL; ``` 如果返回记录且 `parttype='r'`(范围分区),则说明已经是分区主表。 如果没有返回记录,则说明该表是普通表。 2. 如果表是普通表(非分区表),则需要将其转换为分区表。但高斯数据库不支持直接转换,必须重建。 重建步骤: a. 备份数据(如果表中有数据) b. 创建新的分区主表(结构相同,并指定分区键) c. 将数据导入新表 d. 重命名或替换表 具体SQL示例(假设原表是普通表,且没有数据): ```sql -- 删除原表(如果无数据) DROP TABLE evo_event.event_log_alarm202601; -- 创建分区主表(指定分区键) CREATE TABLE evo_event.event_log_alarm202601 ( -- 这里定义原表的列 alarm_id BIGINT NOT NULL, alarm_time TIMESTAMP NOT NULL, -- ... 其他列 ) PARTITION BY RANGE (alarm_time); -- 按alarm_time分区 ``` 3. 添加分区: 创建主表后,即可使用 `ALTER TABLE ... ADD PARTITION` 添加分区: ```sql ALTER TABLE evo_event.event_log_alarm202601 ADD PARTITION p20260101 VALUES LESS THAN ('2026-01-02 00:00:00'); ``` 4. 批量创建分区的方法(可选): 如果需要为一段时间创建多个分区,可以使用存储过程循环创建。 5. 注意事项: - 高斯数据库的分区表要求分区键在主表创建时指定(通过 `PARTITION BY`)。 - 分区边界是半开区间:`[start, end)`,即包含下界,不包含上界。 - 分区名称必须在表内唯一。 6. 错误处理: - `ERROR: can not add partition against NON-PARTITIONED table`:必须确保表是分区主表。 - `ERROR: syntax error at or near "PARTITION OF"`:避免使用PostgreSQL的分区语法。 因此,针对用户的问题,核心解决方案是:先确保主表是分区表(通过重建),然后使用 `ALTER TABLE ... ADD PARTITION` 语法添加分区。 重建表的详细步骤(如果表中有数据): 1. 创建临时表备份: ```sql CREATE TABLE evo_event.event_log_alarm202601_temp AS SELECT * FROM evo_event.event_log_alarm202601; ``` 2. 删除原表: ```sql DROP TABLE evo_event.event_log_alarm202601; ``` 3. 创建分区主表(结构同原表,并指定分区键): ```sql CREATE TABLE evo_event.event_log_alarm202601 ( alarm_id BIGINT NOT NULL, alarm_time TIMESTAMP NOT NULL, -- 其他列定义 ... ) PARTITION BY RANGE (alarm_time); ``` 4. 为当前需要的时间范围创建分区(比如从最早时间到最晚时间,或者按需创建): ```sql -- 例如,创建一个默认分区来接收所有数据(如果不想立即分区所有数据,可以先创建一个大分区,后面再拆分) ALTER TABLE evo_event.event_log_alarm202601 ADD PARTITION p_default VALUES LESS THAN (MAXVALUE); ``` 5. 将数据从临时表导入新表: ```sql INSERT INTO evo_event.event_log_alarm202601 SELECT * FROM evo_event.event_log_alarm202601_temp; ``` 6. 删除临时表: ```sql DROP TABLE evo_event.event_log_alarm202601_temp; ``` 7. 然后,再按需添加新的分区(如按天分区): ```sql -- 先删除默认分区(如果有数据需要迁移,则不能直接删除,这里假设默认分区为空) ALTER TABLE evo_event.event_log_alarm202601 DROP PARTITION p_default; -- 然后按天添加分区 ALTER TABLE evo_event.event_log_alarm202601 ADD PARTITION p20260101 VALUES LESS THAN ('2026-01-02 00:00:00'); -- 添加更多分区... ``` 注意:如果默认分区中有数据,删除分区会导致数据丢失。因此,更安全的做法是先创建好需要的分区,然后直接将数据插入到对应的分区。或者,可以在创建分区主表后,直接按时间段创建多个分区,然后将数据重新分配到各个分区(通过INSERT)。 由于重新分配数据可能比较耗时,对于大数据表,建议在业务低峰期操作。 考虑到用户之前创建分区的意图是按天分区(一天一个分区),我们可以在重建主表后,使用存储过程批量创建分区。 批量创建分区的存储过程示例: ```sql CREATE OR REPLACE FUNCTION create_date_partitions( table_name TEXT, start_date DATE, end_date DATE ) RETURNS VOID AS $$ DECLARE curr_date DATE; partition_name TEXT; sql_cmd TEXT; BEGIN FOR curr_date IN SELECT generate_series(start_date, end_date, '1 day') LOOP partition_name := 'p' || to_char(curr_date, 'YYYYMMDD'); sql_cmd := format( 'ALTER TABLE %I ADD PARTITION %I VALUES LESS THAN (%L)', table_name, partition_name, curr_date + 1 ); EXECUTE sql_cmd; END LOOP; END; $$ LANGUAGE plpgsql; -- 调用示例:为表evo_event.event_log_alarm202601创建从2026-01-01到2026-01-31的分区 SELECT create_date_partitions('evo_event.event_log_alarm202601', '2026-01-01', '2026-01-31'); ``` 重要:高斯数据库的语法与PostgreSQL略有不同,上述存储过程在GaussDB中可能需要进行调整(例如,GaussDB可能不支持generate_series函数用于循环日期,需要改用整数循环)。 调整后的版本(使用整数循环): ```sql CREATE OR REPLACE FUNCTION create_date_partitions( table_name TEXT, start_date DATE, end_date DATE ) RETURNS VOID AS $$ DECLARE curr_date DATE; i INT; days INT; partition_name TEXT; sql_cmd TEXT; BEGIN days := end_date - start_date; FOR i IN 0 .. days LOOP curr_date := start_date + i; partition_name := 'p' || to_char(curr_date, 'YYYYMMDD'); sql_cmd := format( 'ALTER TABLE %I ADD PARTITION %I VALUES LESS THAN (%L)', table_name, partition_name, curr_date + 1 ); EXECUTE sql_cmd; END LOOP; END; $$ LANGUAGE plpgsql; ``` 最后,总结解决步骤: 1. 检查目标表是否是分区主表(不是则重建)。 2. 重建表(如果必要)并定义分区键。 3. 使用 `ALTER TABLE ... ADD PARTITION` 添加分区。 4. 可以使用存储过程批量创建分区。 如果用户确认表已经是分区主表,但仍然遇到 `NON-PARTITIONED table` 错误,请检查表名是否写错(包括模式名是否正确)以及当前用户是否有足够的权限。 另外,注意高斯数据库的版本差异:不同版本的分区表语法可能有细微差别,建议参考对应版本的官方文档。 相关问题: §§相关问题§§ 1. 如何在高斯数据库中查询已存在的分区信息? 2. 高斯数据库的分区表有哪些类型(范围分区、列表分区、哈希分区)?如何选择? 3. 分区表的数据迁移和备份有什么特殊注意事项? 4. 分区表在查询时如何利用分区剪枝提高性能? </think> ### 解决高斯数据库分区表创建错误的完整方案 您遇到的错误是由于高斯数据库(GaussDB)的特殊分区语法要求导致的。以下是分步解决方案: --- ### **错误原因分析** 1. **`ERROR: syntax error at or near "PARTITION OF"`** 高斯数据库不支持 PostgreSQL 风格的 `PARTITION OF` 语法[^1] 2. **`ERROR: can not add partition against NON-PARTITIONED table`** 目标表 `event_log_alarm202601` 未被定义为分区主表[^7] --- ### **解决方案:四步创建分区表** #### 步骤 1:创建分区主表(必须先定义分区键) ```sql -- 必须包含 PARTITION BY RANGE 子句 CREATE TABLE evo_event.event_log_alarm202601 ( alarm_id BIGINT NOT NULL, alarm_time TIMESTAMP NOT NULL, -- 分区键字段 device_id VARCHAR(32), ... -- 其他字段 ) PARTITION BY RANGE (alarm_time); -- 🚨 核心:定义分区键 ``` #### 步骤 2:添加具体分区 ```sql -- 使用 VALUES LESS THAN 指定半开区间 [start, end) ALTER TABLE evo_event.event_log_alarm202601 ADD PARTITION p20260101 VALUES LESS THAN ('2026-01-02 00:00:00'); -- 上限=次日0点 ``` #### 步骤 3:验证分区结构 ```sql -- 查看分区定义 SELECT partition_name, high_value FROM pg_partitions WHERE tablename = 'event_log_alarm202601'; ``` 预期输出: | partition_name | high_value | |---------------------|---------------------| | p20260101 | 2026-01-02 00:00:00 | #### 步骤 4:测试数据路由 ```sql INSERT INTO evo_event.event_log_alarm202601 VALUES (1, '2026-01-01 12:34:56', 'DEV001'); -- 应进入p20260101分区 ``` --- ### **批量创建分区脚本** ```sql CREATE OR REPLACE PROCEDURE create_daily_partitions( main_table TEXT, start_date DATE, end_date DATE ) AS $$ DECLARE curr_date DATE; part_name TEXT; upper_bound TIMESTAMP; BEGIN FOR curr_date IN SELECT generate_series(start_date, end_date, '1 day') LOOP part_name := 'p' || to_char(curr_date, 'YYYYMMDD'); upper_bound := (curr_date + 1)::TIMESTAMP; -- 次日0点 EXECUTE format( 'ALTER TABLE %I ADD PARTITION %I VALUES LESS THAN (%L)', main_table, part_name, upper_bound ); END LOOP; END; $$ LANGUAGE plpgsql; -- 调用示例:创建2026年1月全部分区 CALL create_daily_partitions( 'evo_event.event_log_alarm202601', '2026-01-01', '2026-01-31' ); ``` --- ### **关键语法对比** | **操作** | **PostgreSQL 语法** | **高斯数据库语法** | |------------------|---------------------------------------------|-------------------------------------------| | **定义主表** | 可选 | 必须包含 `PARTITION BY RANGE(column)` | | **添加分区** | `CREATE TABLE ... PARTITION OF ...` | `ALTER TABLE ... ADD PARTITION ...` | | **分区边界** | `FOR VALUES FROM ('start') TO ('end')` | `VALUES LESS THAN ('end')` (上限开区间) | | **分区命名** | 自由命名 | 分区名必须在表内唯一 | --- ### **常见错误处理** 1. **表已存在但不是分区表**: ```sql -- 备份后重建 ALTER TABLE evo_event.event_log_alarm202601 RENAME TO event_log_alarm202601_backup; -- 重新创建分区主表 CREATE TABLE evo_event.event_log_alarm202601 (...) PARTITION BY RANGE (alarm_time); ``` 2. **重复分区名错误**: ```sql -- 先删除冲突分区 ALTER TABLE evo_event.event_log_alarm202601 DROP PARTITION IF EXISTS p20260101; ``` 3. **时间格式错误**: ```sql -- 必须使用 ISO 8601 格式 VALUES LESS THAN ('2026-01-02 00:00:00') -- ✅ 正确 VALUES LESS THAN ('2026/01/02') -- ❌ 错误 ``` > **原理说明**:高斯数据库要求分区键在主表创建时通过 `PARTITION BY RANGE` 显式定义,分区作为主表的附属对象通过 `ALTER TABLE` 管理,这与 PostgreSQL 的分区表实现机制存在本质差异[^7][^9]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值