资源描述
Oracle数据库中业务数据文本导出
摘要:本文论述在ORACEL数据库系统中,利用UTL_FILE包将业务数据依照文本文件格式导出的方法、注意事项和通用源代码。
关键词:ORACLE、UTL_FILE、文本文件格式、导出
一、引言
在日常的数据库操作过程中,经常有将数据库中的部分业务数据导出的需求,用以提供给相关人员,其再利用第三方的工具或者其他数据库系统载入数据。主要用于查看、分析和统计、数据搬移等不同的目的。几乎所有的数据库系统,都拥有自身的导入和导出数据方式和工具。例如:ORACLE数据库中的EXP和IMP工具、SYBASE中的BCP工具等等。需求方对于导出的数据的文件格式和使用场合的要求也是多种多样。最通用的一种方式,就是将数据以文本文件的格式导出。本文论述ORACEL数据库系统中,利用UTL_FILE包将业务数据依照文本文件格式导出的方法、注意事项和通用源代码。
二、数据输入输出及UTL_FILE包
Oracle数据库中,非常著名的导入和导出工具就是EXP和IMP。这是两个命令行的工具,用来抽取表、模式,或从一个Oracle实例中抽取整个数据库的定义,然后导入到另一个实例或模式。但是这两个工具仅仅适用于Oracle数据库系统自身,导出的文件是二进制格式的文件,只有Oracle自身认识数据的含义和文件的组织结构,故而只能在Oracle数据库中相互成对地使用。在异种数据库导入数据时,无法使用EXP导出的数据文件完成数据的正常入库工作。几乎所有的数据库都支持文本格式的数据正常载入数据库。
在Oracle系统中,数据库中的业务数据导出成文本文件,同时不借助第三方开发的工具,主要有两种方式。其一,使用Oracle的SQL*PLUS工具中的SPOOL命令。但是,该种方式在客户端方式工作时,会产生大量的网络数据传输和终端数据的显示输出。在大型网络中占用传输数据带宽和消耗传输时间,所以该方式不适宜导出大量的数据。同时,在生成的数据文本文件中会附加SQL语句。这些附加的SQL语句在数据文件重新使用时,需要用相关的方法将其从数据文件中删除,增加了文件处理的复杂度。其二,使用Oracle提供的UTL_FILE包。这种方式正是本文讨论的范畴。
Oracle系统中,PL/SQL语言本身不提供任何屏幕输出以及I/O的机制,但是它可以通过所提供的包来完成这些功能。使用UTL_FILE包能够实现在数据库服务器端上读写文件。需要注意,由于UTL_FILE包是在服务器端上读写文件,因此它是受限制的操作系统文件的I/O操作。由UTL_FILE执行的文件操作在操作系统中是作为Oracle用户实现的。Oracle用户是用于运行数据库所需要文件的拥有者,同时它也是组成数据库实例进程的拥有者。这样一来Oracle用户就必须具有操作系统读写所有可访问文件的权限。由UTL_FILE包创建的任何文件将归属于Oracle用户所有。这些文件在创建时就已经具有操作系统为Oracle用户配置的特权。如果其它用户要在UTL_FILE之外访问这些文件的话,就需要操作系统变更这些文件的访问特权。
既然UTL_FILE包涉及到操作系统对文件读写的特权问题,自然也就涉及到使用UTL_FILE包的安全性问题。UTL_FILE包的安全性通过两个方面来实现。
1.UTL_FILE包的使用必须要求用户被授予UTL_FILE包的EXECUTE特权。
2.Oracle数据库通过限制UTL_FILE包可以访问的目录的范围来实现安全运行。
授予用户或角色的UTL_FILE包的EXECUTE特权可以以SYS用户登录,然后执行下面的命令:
GRANT EXECUTE ON UTL_FILE TO USERNAME;
如果希望数据库的所有的用户都可以使用该包,将上面命令中的USERNAME替换为PUBLIC。
系统允许访问的目录由数据库初始化文件中的参数UTL_FILE_DIR说明。每个可以访问的目录都在初始化文件中用下面的参数行指定:
UTL_FILE_DIR=directory_name
参数directory_name所指定的目录在很大程度上与操作系统有关。如果操作系统对大小写敏感,则directory_name也区分大小写字母。当有多个可以访问的目录时,初始化文件中参数UTL_FILE_DIR=又则用小括号引起来,多个目录之间用逗号分开,例如UTL_FILE_DIR=(d:\dump1,e:\dump1)。或者也可以用多行,每行用参数名称带参数,例如:
UTL_FILE_DIR=d:\dump1
UTL_FILE_DIR=e:\dump1
关键要求“一行接一行”,如果中间夹杂其它的参数,只有最后一个UTL_FILE_DIR被认可,其它之间的UTL_FILE_DIR参数不被认可。所有的UTL_FILE_DIR参数项在初始化文件中必须是相邻的。但是,一般不建议使用多个目录,以免获取输出文件时的混乱。初始化参数文件的存放的位置为$ORACLE_HOME环境变量下dbs目录下init<sid>.ora文件,其中<sid>为Oracle的实例名。可以在此文件中查找有无UTL_FILE_DIR条目和增加或者修改此条目。修改前,正常关闭数据库,修改保存完毕后,重新打开数据库,设置参数才能生效(初始化参数文件的修改只有数据库管理员有权限完成)。如果数据库在打开状态下,可以通过数据库视图v$parameter查看UTL_FILE_DIR对应的内容,命令如下:
select * from v$parameter where name='UTL_FILE_DIR'
如果Oracle用户对UTL_FILE_DIR指定的目录没有访问的特权,则UTL_FILE包对目录的访问以及读写将被操作系统禁止。需要注意的一点,即使操作系统对大小写不敏感,在UTL_FILE包中指定的目录与初始化参数文件中可访问的目录之间的比较也是大小写敏感的。
UTL_FILE包涉及的过程和函数如表1所示。
表1 UTL_FILE包的过程和函数表
过程和函数的名称
过程和函数的用途
FOPEN函数
为输入输出以缺省的或指定的最大行大小打开一个文件
FOPEN_NCHAR函数
为输入输出以Unicode方式打开文件
IS_OPEN函数
判断某个文件是否打开
FCLOSE函数
关闭一个文件
FCLOSE_ALL过程
关闭所有打开的文件
GET_LINE过程
从打开的文件中读文本
GET_LINE_NCHAR过程
从一个打开的文件中以Unicode方式读文件
PUT过程
将一个字符串写入文件中去
PUT_NCHAR过程
写一个Unicode字符串到文件中去
NEW_LINE过程
将一个换行符写入文件中
PUT_LINE过程
写一行到一个文件中去,后面加一个换行符
PUT_LINE_NCHAR过程
写一个Unicode行到一个文件中去,后面加一个换行符
PUTF过程
格式化并写输出,与C语言中格式化输出类似
PUTF_NCHAR过程
格式化并将Unicode字符串输出
FFLUSH过程
刷行被缓冲的数据,物理地写所有的缓冲到磁盘中去
使用UTL_FILE包进行文件的读写步骤如下。
(1) 声明一个文件句柄变量,以便调用UTL_FILE例程时使用它标识文件。可以使用UTL_FILE.FILE_TYPE类型声明文件句柄变量。
(2) 声明一个VARCHAR2类型的字符串,作为一个读取文件中一行的缓冲区。
(3) 调用UTL_FILE.FOPEN打开文件或者采用UTL_FILE.FOPEN_NCHAR以Unicode方式打开文件。打开文件时需要指定是读还是写文件,不能够既读又写文件。
(4) 如果读取文件,则调用UTL_FILE.GET_LINE或者UTL_FILE.GET_LINE_NCHAR过程。如果是写文件,则调用UTL_FILE.PUT_LINE或者UTL_FILE.PUT_LINE_NCHAR过程。
(5) 完成操作后,调用UTL_FILE.FCLOSE关闭文件。使用UTL_FILE包引发的异常有以下几种,如表2所示。
表2 UTL_FILE包引发的异常
异常描述
引发条件
引发函数或过程
INVALID_PATH
非法或不能访问的目录或文件名
FOPEN
INVALID_MODE
非法打开模式
FOPEN
INVALID_FILEHANDLE
文件句柄指示的文件没有打开
FCLOSE、GET_LINE、PUT、PUT_LINE、NEW_LINE、PUTF、FFLUSH
INVALID_OPERATION
文件不能按照要求打开,该异常与操作系统的特权有关。该异常也可能在企图对以读方式打开的文件进行写的操作时,或企图对写方式打开的文件进行读操作时引发。
GET_LINE、PUT、PUT_LINE、NEW_LINE、PUTF、FFLUSH
INVALID_MAXLINESIZE
所指定行的最大长度太大或太小
FOPEN
READ_ERROR
在读操作期间出现的操作系统错误
GET_LINE
WRITE_ERROR
在写操作期间出现的操作系统错误
PUT、PUT_LINE、NEW_LINE、PUTF、FFLUSH、FCLOSE、FCLOSE_ALL
INTERNAL_ERROR
用于说明发生了某种内部错误
所有的函数或者过程
NO_DATA_FOUND
读操作中遇到了文件结束符
GET_LINE
VALUE_ERROR
输入文件大于GET_LINE中指定的缓冲区
GET_LINE
CHARSETMI-SMATCH
一个文件以FOPEN_NCHAR参数打开,但后来I/O操作用了非Unicode字符函数,如PUTF、GET_LINE
PUTF、GET_LINE
UTL_FILE中的所有操作都使用文件句柄实现。所谓文件句柄就是PL/SQL中用来标识文件的值,所有的文件句柄都是UTL_FILE.FILE_TYPE类型。其作为FOPEN的返回并作为IN类型参数传递给UTL_FILE中其它子程序使用。
关于UTL_FILE包涉及的过程和函数的使用说明,读者可阅读谈竹贤等编著的《Oracle 9i PL/SQL从入门到精通》一书中第17章输入与输出,或者查阅其它有关的Oracle文档资料。
一般情况下,使用UTL_FILE包为了文件内容的输入在Oracle数据库系统中几乎不会用到,而输出用得较为普遍。文本文件的数据在导入Oracle数据库时,自身有非常优秀的工具SQLLDR,无需用到UTL_FILE包的文件输入过程。
三、 问题及解决方法
1. 注意问题
问题(1):当根据业务需求,导出Oracle数据库中有关业务数据时,可以分析出数据分布主要有两种。第一种,所需的业务数据完整地分布在一张数据库的表中;第二种,所需业务数据分散地分布在多张表中,需要多张表相互关联,才能获得所需业务数据。针对第一种情况,只需要导出数据库中该表的内容即可。对于第二种情况,可以通过SQL的DDL语句将相关联的表的对应内容组合,重新生成一张数据库中的新的表,把情况转化成第一种情况来处理。但是,此种方法势必占用数据库系统的资源,造成数据库数据的冗余。当业务数据导出工作完成以后,该生成的表就失去了其存在的价值。为了节省数据库系统中的有限存储资源,再将其从数据库中清除,而清除工作又将占用系统资源。
问题(2):导出Oracle数据库有关业务数据时,从数据完整性取向上也分为两种。第一种,横向截取。无论所需的业务数据在一张表中,还是分布在多张关联的表中,有些字段是需求方需要的,也是可以提供的,有些字段是不需要的,或者出于安全保密考虑是我们不希望提供的。在导出数据时,需要在字段上做出选择,也就是所谓的横向截取。第二种,纵向截取。当获取想要的业务数据时,在信息单元组成的条件满足后,或许会有一些逻辑条件来筛选有关的记录,或者仅仅需要其中的一部分数据,并不需要一个完整的记录集合。这时,需要对数据在纵向上进行一个截取。
问题(3):导出业务数据再加工的工具分类也可以主要分为两类。第一类,导入应用软件。例如,将生成的文本数据文件导入到EXCEL程序中。此种分类只需获知字段的分隔符号,将此信息提供给应用程序即可,数据导入应用程序的工作由应用程序负责完成。第二类,导入到其它数据库中。例如将生成的文本数据导入SQL Server、Sybase等数据库中。在数据库导入前,一般需要在数据库中将导入数据的表结构先行建好,才能完成数据库数据导入工作。如果能在导出数据的同时,将数据的表结构导出成为相应的SQL语言的DDL语句,那么非常方便其它工作人员在其它数据库中创建对应的表结构。同时,这个SQL和DDL语句也可以作为导出数据的咯咯组成字段的说明和数据类型的说明。当然,由于各个数据库厂商对自身产品中数据类型定义的不一致性,SQL的DDL语句的数据类型也是根据导入数据库系统做适应性的调整。
问题(4):导出数据字段的数据类型问题。Oracle数据库的数据类型与其它的数据库系统相比,它的数据类型不多。Oracle在表示数据方面比其它数据库系统来说,要省去许多关键字。Oracle只用number(m,n)就可以表示任何复杂的数字数据。其它如日期类型等也简单得多,只用date表示日期和时间。但Oracle中一些复杂的数据类型,如Blob、Clob、Bfile、Long、Raw等类型,在日常的普通业务数据表中不常使用到。另外,这些数据类型的字段在数据以文本方式导出时会有问题。因而,一旦在需要导出的业务数据的表中含有这些数据类型字段的时候,数据导出就会出现问题。
问题(5):其它一些需要在数据导出过程中考虑的问题。导出数据的各个字段之间的分割符号,根据业务的需求,有可能是不一致的。为了适应客户化的字段分隔符需要,程序能够根据客户的喜好自行定义字段分割符号。有些时候,受到储存介质的大小等影响,需要将导出的数据分割成多个大小相同的文件,程序应能够根据文件的大小或者记录数的数目来分割文件,满足客户的需求。任何操作系统上,文件的大小都不是无限制的。例如,在UNIX和Windows上一个文件一般最大只允许到2GB,再大需要对操作系统的一些参数和配置做些修改。当导出数据文件的信息量大于2GB时,操作系统就没有办法继续导出数据,从而造成程序写错误,异常发生,因而程序应该能够主动识别到此种情况,避免前功尽弃。另外,Oracle 8.0.5之前,文件读写的缓冲区最大值到1024,故而对于该版本之前的Oracle数据库在写数据的时候,需要注意当读写的内容长度大于此数值,系统也会出现读写操作错误。即使在此版本及以后的数据库中,也需要特别声明FOPEN函数中max_linesize参数,才能避免受到1024大小的限制,但它仍然有限制,上限为32767。一般情况下,一行记录的组成长度不会大于该值。还有,导出大量的数据时,可能较耗费时间,随时需要掌握目前数据的导出情况,这就需要有一个类似日志的文件可以记录导出数据的状况。可以通过阅读日志文件,从而掌握数据导出的情况以及出错的位置等信息。
2. 解决方法
上文中提到通用源代码设计中需要考虑处理的问题。为了能够让程序最大范围的通用,必须对这些问题有一个很好的解决方法。
针对问题(1):不希望在业务数据涉及多张数据库中的表的时候,需要先行生成一张新的数据表用来包含需要导出的数据内容,因为这样较浪费系统资源。但如果不在乎系统资源的浪费,先行生成一张数据表,也会使问题简化成对单张表的处理。在数据库概念中有一个视图的概念,而这个概念在数据库处理数据查询时等同于一张独立的表,可以在涉及多张数据库表的内容的时候,仅仅处理视图的定义,不会重复生成与基础表中一样的数据内容,不会消耗系统大量的资源。同时,视图的定义对后面几个问题的解决也提供了很好的途径,在相关的段落中论述。当不再需要此视图,一个简单的drop语句就可以将其从系统的数据字典中删除。
针对问题(2):横向截取时只对单张表中的字段过滤,对于多张表采用视图概念后就不会存在字段过滤的问题。对于横向截取时候,同样可以用视图的方法来处理,将需要的字段在视图中定义。导出数据的时候,不是选择表名,而是选择视图的名字。对于纵向截取的时候,往往需要逻辑判断条件来过滤数据,就像SQL语句中的where条件一样,因此在程序中需要有输入where条件的地方,同时where条件要能对输出结果起到过滤的作用。
针对问题(3):为了便于需求方容易获知数据的DDL语句,需要能够构造出对应的DDL语句。借助于Oracle数据库数据字典中的dba_tab_columns视图,通过字段column_name、data_type、data_length获知表或视图的字段组成和字段属性。对于上述生成的视图中字段的定义,只要这个视图的定义被系统接受,就会在Oracle数据库数据字典中dba_tab_columns视图内会有对应的视图各个字段的定义属性。当获知这些数据后,就可以通过程序将导出的业务数据和数据结构的DDL语句生成,同样也是以文本文件的方式提供给需求方。
针对问题(4):在建立一个表并定义其中的列时,列必须具有各自特定的数据类型。Oracle的剧本数据类型有varchar2、char、date、long、long raw、number、raw以及rowid。除了这些基本的数据类型外,还存在着一些复杂的数据类型,如Blob、Clob、Bfile等等。在普通的业务数据库中,一般大量用到varchar2、char、date、number数据类型,其它的数据类型较少用到。有些数据类型是二进制数据,没办法以文本方法导出,即使导出也将会是无法辨认的一堆乱码符号。通用程序需要在数据导出前,做相关的数据类型的检查,排除复杂的数据类型情况,并且给出有关的提示信息说明出现此类问题。在这些数据类型处理方面,有两点需要注意。其一,对于数字型变量的数据类型定义。Oracle官方资料中认为只有一种类型,那就是number,同时又说number有如下的一些子类型:DEC、DECIMAL、DOUBLE PRECISION、FLOAT、INTEGER、INT、NUMRIC、REAL、SMALLINT。除以DOUBLE PRECISION、FLOAT,其它的子类型在表中定义完成后,数据库生成相关的表,在dba_tab_columns中字段的数据类型都是number。唯独DOUBLE PRECISION、FLOAT子类型,在该视图中数据类型不是用number表示,而是恰恰用float数据类型来表示,并且标明精度为126位。因此,可以这样认为,在Oracle数据库中数据字典的相关视图中,对表中字段类型为数字型的数据类型标识符不是number就是float。其二,对于日期时间型数据的处理。Oracle只用date表示日期和时间,在日期时间的文本输出上应该使用一个通用的惯例格式,那就是年月日加空格再加24小时制的时分秒,时分秒之间以冒号分割。依此标准,客户在获取数据导入自己的数据库的时候,日期时间格式就有了一个标准,便于数据的分拣。
针对问题(5):由于客户可能对字段的分割符号有特定的要求,程序应该能够允许输入特定的分割符号,在导出的文本文件中每行字段之间的分割就使用用户指定的分割符号。因为储存介质的大小等影响或者某些特殊的要求,需要对文件分大小,程序中需要跟踪导出数据的大小,当数据大小达到指定的数值的时候,停止写该文件,重新生成一个新的文件来写数据。同样,客户需求多少行数据生成一个文件,程序中需要有对应的计数器来计数,当达到指定的行数的时候,关闭正在写的文件。也需要监控文件的大小是否接近操作系统规定的最大值。有些时候,导出的文件很大,需要知道程序目前已经完成的工作量和数据处理出错的信息,在程序中需要书写监控文件来监控程序的运行情况。
四、 源代码
以上是对提出的问题的解决思路和办法,将其应用到实际程序中,编写通用程序代码,在源代码语句中,有对相关语句的注释文字,如下所示。
create or replace procedure chenhua_backup_table(
owner in varchar2,
tableorview in varchar2,
filename in varchar2,
field_split_sign in char,
query in varchar2 default null,
filesize in number default 0,
file_rows in number default 0)
is
v_ddl_statement varchar2(32767);--构筑表或者试图的定义
v_sql_statement varchar2(32767);--构筑游标的定义
v_row_content varchar2(32767);--导出每行记录的内容
v_monitor varchar2(1000);--监控文件记录的内容
v_file_path varchar2(100);--文件存放的目录
v_current_filename varchar2(100);--分多个文件时区分目前所处的文件
v_get_ddl_part varchar2(2000);--截断表或试图的定义便于输出
v_row_count number :=0;--记录输出的行数
v_writed_bytes number :=0;--记录输出的字节数
current_file_row number :=0;--目前数据文件中的行数用以额定行数文件用途
current_filesize number :=0;--目前数据文件的大小用以额定大小文件用途
locate number :=0;--定位变量
v_reflush_cnt number :=0;--从输出缓存中写入文件的行数
v_next_file_seq number :=2;--下一个数据文件的序号
v_errinfo varchar2(200);--错误信息描述
type ref_cursor is ref cursor;--参考游标
real_cursor ref_cursor;--定义一个参考游标变量
v_fileHandle UTL_FILE.FILE_TYPE;--数据文件句柄
v_fileHandle_log UTL_FILE.FILE_TYPE;--日志文件句柄
v_fileHandle_ddl UTL_FILE.FILE_TYPE;--表或试图定义文件句柄
E_NOT_SUPPORT_TYPE exception;--不支持数据类型错误
E_TABLE_NOT_FOUND exception;--指定的表或试图不存在错误
E_QUERY_CONDITION_WRONG exception;--条件语句输入错误
E_OVER_1024BYTES exception;--输出内容大于1024字节
cursor c_tab_column(p_tablename varchar2,
p_owner varchar2) is
select column_name,data_type,data_length from dba_tab_columns
where owner = p_owner and table_name = p_tablename
order by column_id;--获取导出数据字段信息的游标
begin
begin
--系统初始化文件中特别指定存放文件的目录询问你的管理员变更该信息
v_file_path:='/fee/bigcustdata';
--开始书写监控日志文件
v_fileHandle_log :=UTL_FILE.FOPEN(v_file_path,filename||'_LOG','W');
v_monitor:=to_char(sysdate,'YYYY/MM/DD HH24:MI:SS')||chr(10)||
'SYSTEM BACKUP TABLE '||owner||'.'||tableorview||chr(10)||
'PROGRAM DESIGNED BY CHENHUA @COPYRIGHT 2004';
UTL_FILE.PUT_LINE(v_filehandle_log,v_monitor);
UTL_FILE.Fflush(v_FileHandle_log);
--如果有条件语句情况下在日志文件中书写条件
if length(query)>0 then
v_monitor:='-----------------------------------------------------'||chr(10)||
'Note BACKUP CONDITION :'||query||chr(10)||
'-----------------------------------------------------';
UTL_FILE.PUT_LINE(v_filehandle_log,v_monitor);
UTL_FILE.Fflush(v_FileHandle_log);
end if;
--创建表或试图的定义语句以及获取读出文本内容的SQL语句
v_filehandle_ddl := UTL_FILE.FOPEN(v_file_path,filename||'_DDL','W');
v_ddl_statement:='create table '||owner||'.'||tableorview||'(';
v_sql_statement:='select ';
for v_col in c_tab_column(upper(tableorview),upper(owner)) loop
--定义语句
if(v_col.data_length=null) then
v_ddl_statement:=v_ddl_statement||v_col.column_name||' '||v_col.data_type||',';
else
v_ddl_statement:=v_ddl_statement||v_col.column_name||' '||v_col.data_type||'('||v_col.data_length||')'||',';
end if;
--读出文本内容的SQL语句
if v_col.data_type <> 'DATE' and v_col.data_type<>'NUMBER' and v_col.data_type<>'CHAR'
and v_col.data_type <> 'VARCHAR2' and v_col.data_type <> 'FLOAT'
and v_col.data_type <> 'VARCHAR' then
raise E_NOT_SUPPORT_TYPE;
end if;
if v_col.data_type = 'DATE' then
v_sql_statement := v_sql_statement || 'to_char(' || v_col.column_name
|| ',''YYYY/MM/DD HH24:MI:SS'')' ||'||'||''''||field_split_sign||''''||'||';
end if;
if v_col.data_type in ('CHAR','VARCHAR2','VARCHAR','NUMBER','FLOAT') then
v_sql_statement := v_sql_statement || v_col.column_name ||'||'||''''||field_split_sign||''''||'||';
end if;
end loop;
--表或试图是否存在
if instr(v_sql_statement,field_split_sign) = 0 then
raise E_TABLE_NOT_FOUND;
end if;
v_sql_statement := substr(v_sql_statement,1,length(v_sql_statement)-length(field_split_sign)-6) || ' from ' || owner || '.' || tableorview ;
v_ddl_statement := substr(v_ddl_statement,1,length(v_ddl_statement)-1)||')';
--特别处理表定义部分的内容长度过长的处理
while (length(v_ddl_statement) >1000) loop
locate:=0;
for i in 1..1000 loop
if(substr(v_ddl_statement,i,1)=',')then
locate:=i;
end if;
end loop;
v_get_ddl_part:=substr(v_ddl_statement,1,locate);
UTL_FILE.PUT_LINE(v_filehandle_ddl,v_get_ddl_part);
v_ddl_statement:=substr(v_ddl_statement,locate+1);
end loop;
UTL_FILE.PUT_LINE(v_filehandle_ddl,v_ddl_statement);
UTL_FILE.FCLOSE(v_filehandle_ddl);
--表或试图的定义ddl语句完成了输出
--判别整表输出还是带有QUERY条件语句的输出
if length(query)>0 then
if instr(upper(query),'WHERE') = 0 then
raise E_QUERY_CONDITION_WRONG;
end if;
v_sql_statement := v_sql_statement||' '||query;
end if;
--开始书写表中的内容
v_filehandle := UTL_FILE.FOPEN(v_file_path,filename,'W');
v_current_filename:=filename;
open real_cursor for v_sql_statement;
loop
fetch real_cursor into v_row_content;
exit when real_cursor%NOTFOUND;
v_writed_bytes:=v_writed_bytes+lengthb(v_row_content);
current_filesize:=current_filesize+lengthb(v_row_content)+1;
--按照用户指定的文件大小分割文件或者文件大小到达1.95G时强行结束文件
if((current_filesize > nvl(filesize,0)*1024*1024 and nvl(filesize,0) > 0) or (current_filesize > 1.95*1024*1024*1024 ) ) then
UTL_FILE.FCLOSE(v_filehandle);
v_monitor:=v_current_filename||' HAS '||current_file_row||' ROWS '||chr(10)||'NOW,BACKUPED '||v_row_count||' ROWS TOTAL'||chr(10);
UTL_FILE.PUT_LINE(v_filehandle_log,v_monitor);
v_filehandle := UTL_FILE.FOPEN(v_file_path,filename||'_'||v_next_file_seq,'W');
v_current_filename:=filename||'_'||v_next_file_seq;
v_next_file_seq:=v_next_file_seq+1;
current_file_row:=0;
current_filesize:=0;
end if;
--按照用户指定的记录行数分割文件优先级低于文件的大小
if(current_file_row=file_rows and file_rows<>0) then
UTL_FILE.FCLOSE(v_filehandle);
v_monitor:=v_current_filename||' HAS '||current_file_row||' ROWS '||chr(10)||'NOW,BACKUPED '||v_row_count||' ROWS TOTAL'||chr(10);
UTL_FILE.PUT_LINE(v_filehandle_log,v_monitor);
v_filehandle := UTL_FILE.FOPEN(v_file_path,filename||'_
展开阅读全文