带缓冲区的 utl 文件 oracle

utl file oracle with buffer

我读过这篇关于调优的article UTL_FILE

从技术上讲,方法是在大小小于缓冲区的最大长度时连接记录,并在长度大于时写入整个缓冲区

网站摘录(代码):

         IF LENGTH(v_buffer) + c_eollen + LENGTH(r.csv) <= c_maxline THEN
            v_buffer := v_buffer || c_eol || r.csv;
         ELSE
            IF v_buffer IS NOT NULL THEN
               UTL_FILE.PUT_LINE(v_file, v_buffer);
            END IF;
         v_buffer := r.csv;
         END IF;

所以,我决定将这个移动到一个函数中

-- constants 
  C_CHR                                  CONSTANT VARCHAR2(2)  := CHR(10);
  C_CHRLEN                               CONSTANT PLS_INTEGER  := LENGTH(C_CHR);
  C_MAXLEN                               CONSTANT PLS_INTEGER  := 32767;

function FN_GET_BUFFER(p_rec IN VARCHAR2, p_buffer IN VARCHAR2) RETURN VARCHAR2
  is
  begin
      IF LENGTH(p_buffer) + C_CHRLEN + LENGTH(p_rec) <= C_MAXLEN THEN
        RETURN p_buffer || C_CHR || p_rec;
      ELSE
        IF p_buffer IS NOT NULL THEN
            RETURN p_buffer;
        END IF;
        RETURN p_rec;
      END IF;
  end FN_GET_BUFFER;

下面是我如何调用我的函数,但它没有按预期工作..

procedure export as 
    l_tmp_file_name VARCHAR2(30);
    l_csv_file_name VARCHAR2(30);
    l_file          UTL_FILE.FILE_TYPE;
    l_buffer        VARCHAR2(32767);

    CURSOR cur_table
    IS
    SELECT * FROM table    
begin
    l_tmp_file_name := 'file.tmp';
    BEGIN
        l_file := UTL_FILE.FOPEN(C_DIRECTORY_PATH, l_tmp_file_name,'A',C_MAXLEN);          
        FOR rec IN cur_table
        LOOP
            l_rec := CONVERT(rec.id || ',' || rec.user ,'AL32UTF8');            
            l_buffer := l_buffer || FN_GET_BUFFER(l_rec, l_buffer);
            if l_buffer is NOT NULL then
                UTL_FILE.PUT_LINE(l_file, l_buffer);
                l_buffer := NULL;
            end if;
       END LOOP rec;    
       UTL_FILE.FCLOSE(l_file);   
       l_csv_file_name := 'file.csv';
       UTL_FILE.FRENAME(src_location => C_DIRECTORY_PATH, src_filename => l_tmp_file_name, dest_location => C_DIRECTORY_PATH, dest_filename => l_csv_file_name, overwrite => FALSE);
   EXCEPTION
      WHEN OTHERS THEN
          UTL_FILE.FCLOSE(l_file);
          UTL_FILE.FREMOVE(location => C_DIRECTORY_PATH, filename => l_tmp_file_name);
    END;
end export;

问题是我得到

1,user1
2,user2
3,user3
4,user4
5,
user5
6,user6
7,user7
8,user8
9,user9
10,user10
11,user11
12,user12
13,user13
14,
user14
15,user15
16,user16
17,user17
18,user19

如您所见,在 4 条记录之后,缓冲区是 'full' 所以它改为写入 缓冲区是 user14 而不是全部写在同一行

谢谢

问题不在于你的函数本身,而是你在调用后所做的测试:

if l_buffer is NOT NULL then
            UTL_FILE.PUT_LINE(l_file, l_buffer);
            l_buffer := NULL;
end if;

l_buffer 始终填充,它永远不会为空。所以测试总是正确的,你为 table 中的每一行写入文件。您需要测试 length of l_buffer 并且仅在长度大于您的限制时写入。

但不要只是更改测试。您需要取消选择 FN_GET_BUFFER() 的逻辑以将缓冲区填充和刷新包含在单个子例程中,否则您将丢失数据。像这样:

FOR rec IN cur_table
LOOP
    l_rec := CONVERT(rec.id || ',' || rec.user ,'AL32UTF8');            

    IF LENGTH(l_buffer) + LENGTH(C_CHRLEN) + LENGTH(l_rec) > C_MAXLEN THEN
        -- buffer full, write to file 
        UTL_FILE.PUT_LINE(l_file, l_buffer);
        l_buffer := l_rec;
    ELSIF LENGTH(l_buffer) = 0 THEN 
        -- first record
        l_buffer := l_rec;
    ELSE
       -- buffer not full
       l_buffer := _l_buffer || C_CHRLEN || l_rec;
    END IF;
END LOOP rec;
if LENGTH(l_buffer) > 0 THEN
   -- end of table, write last record
   UTL_FILE.PUT_LINE(l_file, l_buffer);
end if;

警告 编码狂野风格,未测试