会员登陆  支付方式  联系我们  在线客服  网站地图       
首页 关于域浪 互联网数据中心业务、主机托管、主机租用、机架租用、带宽租用、机房介绍、机房对比、CCN网络加速 adsl专线、深圳adsl专线 虚拟主机、域名注册、数据库、企业邮局、动态域名 网络安全、网络临近平台、安全服务、防火去墙租用、安全产品、域浪抗DDOS防火墙、NetScreen防火墙 技术支持  
   当前位置:首页 >> 技术支持 >> ORACLE数据库技术 >> 在ORACLE里用存储过程定期分割表
 
精华文章
    一步一步教你Oracle 9...  
    ORACLE碎片整理(2)  
    ORACLE碎片整理(1)  
    关于Oracle 对 Linux ...  
    ORACLE之常用FAQ V1.0...  
    ORACLE之常用FAQ V1.0...  
    Oracle平台应用数据库...  
    Oracle的空间数据库管...  
    Oracle传统基本体系结...  
    Oracle平台下的数据分...  
    Oracle密码文件的创建...  
    Oracle数据库的安全策...  
    双机容错环境Oracle数...  
    Oracle数据库系统使用...  
    ORACLE常见错误代码分...  
    Oracle数据库管理脚本...  
    Oracle中一个日期查找...  
    在ORACLE里用存储过程...  
    Oracle数据库逻辑备份...  
    Oracle数据库系统的安...  
    Oracle数据库处理身份...  
    Oracle数据库的备份及...  
    Oracle数据库表空间恢...  
    案例讨论:Oracle两表...  
    Oracle数据库系统性能...  
    优化Oracle网络设置的...  
    Oracle9i RMAN备份及恢...  
    弥补Oracle数据库访问...  
  更多>>  
   ORACLE数据库技术
 在ORACLE里用存储过程定期分割表
    Oracle数据库里存放着各种各样的数据,其中有一些数据表会随着时间的推移,越来越大。如交友聊天的日志、 短信收发的日志、生产系统的日志、动态网站发布系统的日志等等。这样的信息又和时间紧密相关,有没有办法 让这些日志表能到时间自动分割成历史年月(如log200308,log200309)的表呢? 请看看我用存储过程定期分割表的 方法吧。



一、问题的引出



1.初学数据库时只知道用delete来删除表里的数据。但在Oracle数据库里,大量delete记录后,并不能释放表所占用的物理空间,这里面有一个高水位的概念,所以我们不能用delete来分割表。

2.用重命名(rename)表的方法:

(1).先建一个和原来日志表(假如是log)数据结构一模一样的新表(如log_new),建约束、索引及指定字段的默认值;

(2).重命名表log到log_YYYYMM; 

    要注意的问题是OLTP系统可能会因为DML操作阻碍重命名执行成功,出现ORA-00054资源正忙的错误提示,需要试多次才能成功。

(3).重命名表log_new到log。

    这样应用程序不用修改(受影响的时间仅几秒钟),日志表就被截断分割了。

    上述步骤可以在Oracle里可以用存储过程来实现它们。



二、用存储过程来分割表



    可以看到在重命名表的方法中,步骤(2)是个关键。

    下面这个rename_table过程会在有锁阻碍的情况下用递归的方式重试100次。 

    重命名原始表到目标表的存储过程rename_table:

    create or replace procedure rename_table

    (source_name in varchar2,

    target_name in varchar2,

    times in out number)

    is

    query_str varchar2(4000);

    source_name1 varchar2(64);

    target_name1 varchar2(64);

    cursor c1 is select segment_name from user_segments where segment_name=upper(source_name);

    dummy c1%rowtype; 

    cursor c2 is select segment_name from user_segments where segment_name=upper(target_name);

    dummy2 c2%rowtype; 

    begin

    source_name1:=source_name;

    target_name1:=target_name;

    open c1;

    fetch c1 into dummy;

    -- if c1%found then

    -- dbms_output.put_line(source_name1||'exist!');

    -- end if;

    open c2;

    fetch c2 into dummy2;

    -- if c2%notfound then

    -- dbms_output.put_line(target_name1||'not exist!'); 

    -- end if;

    if c2%notfound and c1%found then

    query_str :='alter table '||source_name1||' rename to '||target_name1;

    execute immediate query_str;

    dbms_output.put_line('rename success!');

    end if;

    close c1;

    close c2;

    exception

    WHEN OTHERS THEN 

    times:=times+1;

    if times<100 then

    -- dbms_output.put_line('times:'||times);

    rename_table(source_name1,target_name1,times);

    else

    dbms_output.put_line(SQLERRM);

    dbms_output.put_line('error over 100 times,exit');

    end if;

    end;

    /

    截断分割log表的存储过程log_history:

    create or replace procedure log_history

    is

    query_str varchar2(32767);

    year_month varchar2(8);

    times number;

    begin

    select to_char(sysdate-15,'YYYYMM') into year_month from dual;

    times:=0;

    query_str :='create table log_new pctfree 10 pctused 80 

    as select * from log where 1=2';

    execute immediate query_str;

    query_str :='alter table log_new add constraints log_'||year_month||'_pk

    primary key (id) tablespace indx nologging pctfree 10';

    execute immediate query_str; 

    query_str :='alter table log_his modify logtime default sysdate';

    execute immediate query_str; 

    query_str :='create index log_'||year_month||'_logtime on log(logtime)

    tablespace indx nologging pctfree 10';

    execute immediate query_str; 

    rename_table('log','log'||year_month,times);

    query_str :='alter table log_new rename to log';

    execute immediate query_str;

    end;

    /

    当然您工作环境的日志表可能和 我这个做例子的日志表结构上有所不同,约束条件、索引和默认值都不尽相同。 只要稍加修改就可以了。



三、用户需要有create any table系统权限(不是角色里包含的权限)



    因为在执行存储过程时,由角色赋予的权限会失效, 所以执行log_history的用户一定要有DBA单独赋予的create any table系统权限。



    最后在OS里定时每月一号凌晨0:00分执行log_history,让存储过程定期分割表。如果要分割的日志表很多,模仿log_history可以写很多类似的存储过程来分割不同项目里的日志表。然后让OS按月,按周或者不定期地执行这些存储过程, 管理员只要查看日志就可以了。



四、其它注意事项 



    如果应用程序有BUG,可能对在用原始日志表产生长期不能释放的锁,执行log_history重命名会不成功。这时DBA可以查看数据字典:

select object_id,session_id,locked_mode from v$locked_object;

select t2.username,t2.sid,t2.serial#,t2.logon_time 

from v$locked_object t1,v$session t2 

where t1.session_id=t2.sid order by t2.logon_time;



    如果有长期出现的一模一样的列(包括登录时间),可能是没有释放的锁。我们要在执行分割日志表的存储过程前,用下面SQL语句杀掉长期没有释放非正常的锁:

alter system kill session 'sid,serial#';



五、结束语



    用上面介绍的存储过程定期分割日志表有很大的灵活性。历史数据不仅查询方便,转移和备份起来也都很容易。Unix和Windows平台的都可以使用。对服务器硬盘空间较小的中小型公司意义尤其明显。
  • 上一篇文章: Oracle中一个日期查找的误区
  • 下一篇文章: Oracle数据库逻辑备份的SH文件
  • 域浪网络ISP经营许可证 深圳地址:深圳市罗湖区宝安北路国际商品交易大厦七楼C30室
    Tel:0755-82266883/82267566 Fax:0755-82261966
    邮编:518000 
                        Copyright © 2006-2008 elang.cn All Rights Reserved 深圳市域浪网络技术有限公司版权所有