Oracle写本地文件

Oracle写本地文件是指写到运行Oracle的主机上,而不是运行该脚本的机器上。
说起来有点拗口,实际上就是无论在哪里执行这个过程,生成的文件始终都是在服务器上的。
下面过程实现了这个功能:
logdir是指文件存放路径。有Oracle的directory指定:
create or replace directory log_dir  as '/oracle/admin/orcl';

创建带有clob字段的表:
create table testclob(obj_type varchar2(255), obj_content clob);

插入测试数据:
insert into testclob(obj_type, obj_content)
select 'PROCEDURE', dbms_metadata.get_ddl('PROCEDURE', o.object_name)
from dba_objects o
where o.owner = user
and o.object_type = 'PROCEDURE'

创建存储过程:
create or replace procedure sp_writelog2file(logdir varchar2,
                                             filename varchar2,
                                             writemode char := 'W') as
    type tbl_result is table of varchar2(2000) index by pls_integer;
    v_res tbl_result;
    type tbl_clob is table of clob index by pls_integer;
    v_clobs   tbl_clob;

    v_filename   varchar2(255) := filename;
    v_logdir     varchar2(255) := logdir;
    v_buffer     pls_integer := 2000;
    v_offset     pls_integer := 1;
    v_filehandle utl_file.file_type;

    function f_readclob(varclob clob)
    return tbl_result as
        v_maxbuff pls_integer := 2000;
        v_cloblen pls_integer := length(varclob);
        v_result tbl_result;
        v_buffer pls_integer := v_maxbuff;
        v_offset pls_integer := 1;
        v_nextpos pls_integer := 1;
        v_prevpos pls_integer := 1;
        v_maxstep pls_integer := 20;
        v_nth    pls_integer := v_maxstep;
    begin
        while v_nextpos <> 0 loop
            v_nextpos := dbms_lob.instr(varclob, chr(10), 1, v_nth);
            v_buffer  := (case when v_nextpos = 0 then v_cloblen else v_nextpos end) - v_prevpos;

            if (v_buffer > v_maxbuff and v_nextpos <> 0) then
                v_nth := v_nth - 3;/*超过最大缓冲区,指针退3个*/
            elsif (v_buffer < 3*v_maxbuff/4 and v_nextpos <> 0) then
                v_nth := v_nth + 3;/*未达最大缓冲区的3/4,指针进3个*/
            else
                dbms_lob.read(varclob, v_buffer, v_offset, v_result(nvl(v_result.last, 0) + 1));
                v_prevpos := v_nextpos;
                v_nth := v_nth + v_maxstep;
                v_offset := v_offset + v_buffer;
            end if;
        end loop;
        return v_result;
    end f_readclob;
begin
    v_filehandle := utl_file.fopen(v_logdir, v_filename, writemode);
    if(utl_file.is_open(v_filehandle)) then
        select t2.obj_content
          bulk collect into v_clobs
          from testclob t2;
        for i in 1 .. v_clobs.count loop
            v_res := f_readclob(v_clobs(i));
            for j in 1 .. v_res.count loop
                utl_file.put_line(v_filehandle, v_res(j));
            end loop;
        end loop;
    end if;
    utl_file.fclose(v_filehandle);
exception when others then
    utl_file.fclose(v_filehandle);
end sp_writelog2file;

调用存储过程,将testclob的内容写到主机上的文件中:
call sp_writelog2file('LOG_DIR', 'lyon.txt');

对于clob对象的读取,采用了分段截取的算法。截取标识为换行符(chr(10))。
每次步长为20个换行符间隔。假设每次截取长度最大值为N(这里N=2000)。该间隔区间内,如果字符数范围在[N*3/4, N]之间,则直接截取。
如果小于3/4 N长度,则指针标识向前推3个换行符间隔。如果大于N,则向后退3个换行符间隔。保证截取的长度始终在3/4-1个N之间。

请使用浏览器的分享功能分享到微信等