当前位置: 代码网 > it编程>数据库>Oracle > Oracle中将非分区表转换为分区表的最佳实践

Oracle中将非分区表转换为分区表的最佳实践

2026年04月13日 Oracle 我要评论
虽然从 oracle 12.2 开始,技术上已经可以直接通过以下方式转换非分区表为分区表:alter table … modify但一些较为复杂分区场景,还是建议采用在线重定义操作来更改

虽然从 oracle 12.2 开始,技术上已经可以直接通过以下方式转换非分区表为分区表:alter table … modify但一些较为复杂分区场景,还是建议采用在线重定义操作来更改为分区表。

什么是表重定义(table redefinition)?

在线表重定义(online table redefinition) 允许在表正在被使用的情况下对其进行结构变更,且零停机时间

它是如何工作的?

oracle 通过创建临时表和同步机制来实现在线重定义:

  1. 你创建一个具有目标结构的临时表(interim table)
  2. oracle 将数据从原表复制到临时表
  3. 在此期间产生的任何变更都会被同步
  4. 最后,oracle 以原子方式将原表与临时表进行交换

测试案例

1. 创建 sequence(用于生成 order_id)

create sequence szr.customer_orders_seq
  start with 1
  increment by 1
  nocache
  nocycle;

2. 创建测试用的非分区表 customer_orders(11g 兼容)

create table szr.customer_orders (
    order_id        number        primary key, 
    order_date      date          not null,
    customer_id     number        not null,
    customer_name   varchar2(120),
    product_code    varchar2(50),
    quantity        number(8),
    unit_price      number(12,2),
    total_amount    number(14,2)  generated always as (quantity * unit_price) virtual,
    order_status    varchar2(20)  default 'pending',
    created_by      varchar2(60)
) tablespace users;

-- 创建 trigger 实现自增主键
create or replace trigger szr.customer_orders_trg
before insert on szr.customer_orders
for each row
begin
    if :new.order_id is null then
        select szr.customer_orders_seq.nextval
          into :new.order_id
          from dual;
    end if;
end;
/

3. 插入一些测试数据(覆盖不同年份)

insert into szr.customer_orders (order_date, customer_id, customer_name, product_code, quantity, unit_price, order_status, created_by)
select 
    add_months(to_date('2020-03-10','yyyy-mm-dd'), level*1) as order_date,
    5000 + mod(level, 300) as customer_id,
    'customer_' || to_char(5000 + mod(level, 300)) as customer_name,
    'prod_' || lpad(mod(level, 99)+1, 3, '0') as product_code,
    mod(level, 50) + 1 as quantity,
    round(dbms_random.value(10, 500), 2) as unit_price,
    case mod(level, 5) 
        when 0 then 'completed' 
        when 1 then 'shipped' 
        when 2 then 'pending' 
        else 'cancelled' 
    end as order_status,
    'user_' || mod(level, 10) as created_by
from dual
connect by level <= 1200;

commit;

-- 查看原表记录数和数据分布
select count(*) as total_rows from szr.customer_orders;

select min(order_date), max(order_date) from szr.customer_orders;

select trunc(order_date,'mm'), count(*) from szr.customer_orders group by trunc(order_date,'mm') order by 1;

4. 检查表是否支持重定义(使用 cons_use_pk)

set serveroutput on;
begin
  dbms_redefinition.can_redef_table(
    uname        => 'szr',
    tname        => 'customer_orders',
    options_flag => dbms_redefinition.cons_use_pk
  );
  dbms_output.put_line('表支持重定义(cons_use_pk):成功');
end;
/

5. 创建具有目标分区结构的临时表(interim table)

create table szr.customer_orders_int
tablespace users
partition by range (order_date)
interval (numtoyminterval(1, 'month'))    
(
  partition p_before_2022 values less than (to_date('2022-01-01','yyyy-mm-dd'))
)
as 
select * from szr.customer_orders 
where 1=0;

-- 查看临时表当前分区
select partition_name, high_value 
from user_tab_partitions 
where table_name = 'customer_orders_int';

6. 启动重定义过程(使用 cons_use_pk)

set serveroutput on;
begin
  dbms_redefinition.start_redef_table(
    uname        => 'szr',
    orig_table   => 'customer_orders',
    int_table    => 'customer_orders_int',
    options_flag => dbms_redefinition.cons_use_pk
  );
  dbms_output.put_line('start_redef_table 执行完成');
end;
/

大表场景:可在 start_redef_table 前开启并行(如 alter session force parallel dml parallel 4;)。

回退操作:如果执行过程中发生错误,可以使用以下语句回退

begin
  dbms_redefinition.abort_redef_table(
    uname      => 'szr',
    orig_table => 'customer_orders',
    int_table  => 'customer_orders_int'
  );
end;
/

7. 复制原表的依赖对象

set serveroutput on;
declare
  l_num_errors  pls_integer;
begin
  dbms_redefinition.copy_table_dependents(
    uname            => 'szr',
    orig_table       => 'customer_orders',
    int_table        => 'customer_orders_int',
    copy_indexes     => 0,                    -- 不自动复制索引(主键索引已存在)
    copy_triggers    => true,
    copy_constraints => false,                -- 关键:关闭约束复制,避免 not null 冲突
    copy_privileges  => true,
    ignore_errors    => true,
    num_errors       => l_num_errors,
    copy_statistics  => false
  );
  dbms_output.put_line('copy_table_dependents 执行完成,错误数: ' || l_num_errors);
end;
/

8. 同步期间产生的新 dml 数据(推荐执行)

begin
  dbms_redefinition.sync_interim_table(
    uname      => 'szr',
    orig_table => 'customer_orders',
    int_table  => 'customer_orders_int'
  );
  dbms_output.put_line('sync_interim_table 执行完成');
end;
/

9. 完成重定义

begin
  dbms_redefinition.finish_redef_table(
    uname      => 'szr',
    orig_table => 'customer_orders',
    int_table  => 'customer_orders_int'
  );
  dbms_output.put_line('finish_redef_table 执行完成,表已成功转换为分区表');
end;
/

10. 验证结果

select table_name, partitioning_type, interval 
from user_part_tables 
where table_name = 'customer_orders';

select partition_name, high_value, num_rows 
from user_tab_partitions 
where table_name = 'customer_orders' 
order by partition_position;

select count(*) from szr.customer_orders;

-- 删除临时表
drop table szr.customer_orders_int purge;

-- 收集统计信息
begin
  dbms_stats.gather_table_stats('szr', 'customer_orders', cascade => true);
end;
/

总结

通过以上步骤,我们成功使用在线表重定义技术将普通表 customer_orders 转换为了按月间隔分区表。整个过程无需停机,对业务影响最小,特别适合需要高可用7x24 运行的生产环境。

在线重定义的优势在于:

  • ✅ 零停机时间
  • ✅ 完整的依赖对象迁移
  • ✅ 支持回滚操作
  • ✅ 数据一致性有保障

以上就是oracle中将非分区表转换为分区表的最佳实践的详细内容,更多关于oracle非分区表转换为分区表的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com