UTL_FILE 使用追加模式创建重复 headers

UTL_FILE using append mode creates repeating headers

如何在UTL_FILE包中使用"A"ppend模式,但只创建一个header(不再重复)?是否可以?我正在追加数据,但每次追加时,都会创建重复的 headers。

我的代码:

CREATE OR REPLACE PROCEDURE p_test AS
CURSOR c_test IS
select blah, blah from dual;

 v_file  UTL_FILE.FILE_TYPE;
 v_header   varchar2(25);

BEGIN
 v_file := UTL_FILE.FOPEN(location  => 'my_dir',
                       filename     => 'filetest09102019.csv',
                       open_mode    => 'A',
                       max_linesize => 32767);
 If file exists = 0 then        --using fgetattr; if I use 1, repeating headers will print
     v_header := 'col1, col2, col3';
     utl_file.put_line (v_file, v_header); 
 Else null; end if;     --unfortunately headers did not print at all when false/0                  
 FOR cur_rec IN c_test LOOP
UTL_FILE.PUT_LINE(v_file, data from c_test );
END LOOP;
UTL_FILE.FCLOSE(v_file);

EXCEPTION
WHEN OTHERS THEN
UTL_FILE.FCLOSE(v_file);
END;

如果您多次调用此过程,则每次调用该过程时都会将 header 追加到文件中。

您可以先检查文件是否存在,然后再附加 header,例如使用 fgetattr 检测文件是否将被附加 Check if a file exists?

或者,修改您的代码,使其仅调用一次过程并一次写入所有数据,而不追加。

你可以通过一些设计来解决这个问题。目前,您有一个程序以追加模式打开文件,将文件头写入其中,然后将数据写入其中。您需要的是一个用于打开文件的子例程。这个程序将是 实现以下逻辑:

  1. 测试文件是否存在(like this one)
  2. 如果文件不存在,以写入模式创建文件,写入文件头,然后关闭文件
  3. 以追加模式打开文件。

您现有的过程现在只需调用上述例程并将数据写入打开的文件。这个东西(使用借来的和未经测试的代码):

CREATE OR REPLACE PROCEDURE p_test AS
CURSOR c_test IS
select blah, blah from dual;

 v_file  UTL_FILE.FILE_TYPE;
 v_header   varchar2(25)  := 'col1, col2, col3';

 function open_file (p_filename in varchar2
                     , p_dirname in varchar2
                     , p_header in varchar2
                      )
   return UTL_FILE.FILE_TYPE
 is
    fh UTL_FILE.FILE_TYPE;
    l_fexists boolean;
    l_flen   number;
    l_bsize  number;
    l_res    number(1);   
  begin
    utl_file.fgetattr(upper(p_DirName), p_FileName, l_fexists, l_flen, l_bsize);
    if not l_fexists  then
      fh := UTL_FILE.FOPEN(location    => p_DirName,
                           filename     => p_FileName,
                           open_mode    => 'W',
                           max_linesize => 32767);     
      utl_file.put_line (fh, p_header); 
      utl_file.fclose(fh);
    end if;
    fh := UTL_FILE.FOPEN(location    => p_DirName,
                         filename     => p_FileName,
                         open_mode    => 'A',
                         max_linesize => 32767);     
    return fh;
  end open_file;

BEGIN
 v_file := open_file 'my_dir', 'filetest09102019.csv', v_header);                  

  FOR cur_rec IN c_test LOOP
    UTL_FILE.PUT_LINE(v_file, data from c_test );
  END LOOP;
UTL_FILE.FCLOSE(v_file);

EXCEPTION
WHEN OTHERS THEN
UTL_FILE.FCLOSE(v_file);
END;

严格来说 open_file () 在此示例中不需要私有过程。但总的来说,我认为将低级内容隐藏在单独的过程中是一种很好的做法,因为它使代码的主体更易于阅读。此外,我们经常希望在多个地方(针对不止一种类型的文件)执行此操作,因此封装起来很方便。