Как указать максимальное количество строк для записи в один CSV-файл в Oracle? - PullRequest
0 голосов
/ 10 ноября 2018

Я использую процедуру, описанную ниже в this для выгрузки данных в csv. Это записывает данные в один CSV-файл test.csv. Если таблица очень большая, я не хочу записывать в один CSV-файл. Я хочу указать порог 10k в него с именем файла как

test_sequenceNumber _timeOfOpeningInYYYYMMDDHHMMSSsss.csv

так, например, если количество строк в таблице 15k, то у меня должно быть 2 файла:

test_1_20181112012830000.csv 
test_2_20181112012835005.csv

Как мы можем добиться этого, изменив приведенный ниже SP?

create or replace procedure dump_table_to_csv
    ( p_tname in varchar2
    , p_dir in varchar2
    )
as
    l_output utl_file.file_type;
    l_theCursor integer default dbms_sql.open_cursor;
    l_columnValue varchar2(4000);
    l_status integer;
    l_query varchar2(1000)
        default 'select * from ' || p_tname;
    l_colCnt number := 0;
    l_separator varchar2(1);
    l_descTbl dbms_sql.desc_tab;
    p_filename varchar2(100) := 'test.csv' ;
begin
    l_output := utl_file.fopen( p_dir, p_filename, 'w' , 32760); 
    execute immediate 'alter session set nls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';

    dbms_sql.parse( l_theCursor, l_query, dbms_sql.native );
    dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );

    for i in 1 .. l_colCnt loop
        utl_file.put( l_output, l_separator || '"' || l_descTbl(i).col_name || '"' );
        dbms_sql.define_column( l_theCursor, i, l_columnValue, 4000 );
        l_separator := ',';
    end loop;

    utl_file.new_line( l_output );

    l_status := dbms_sql.execute(l_theCursor);

    while (dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
        l_separator := '';

        for i in 1 .. l_colCnt loop
            dbms_sql.column_value( l_theCursor, i, l_columnValue );
            utl_file.put( l_output, l_separator || l_columnValue );
            l_separator := ',';
        end loop;

        utl_file.new_line(l_output );
    end loop;

    dbms_sql.close_cursor(l_theCursor);
    utl_file.fclose(l_output );

    execute immediate 'alter session set nls_date_format=''dd-MON-yy'' ';
exception
    when others then
        utl_file.fclose( l_output );
        execute immediate 'alter session set nls_date_format=''dd-MON-yy'' ';
        raise;
end;

EDIT1: Я пытался применить логику mod(num_rows_written,10000) == 0, пришло время закрыть старый файл и запустить новый, но я просто застрял в точке, которая должна быть в верхней части цикла для продолжения той же операции с новым файлом?

1 Ответ

0 голосов
/ 12 ноября 2018

Ну, это довольно простая задача. Все, что вам нужно сделать, это закрыть старый файл и открыть новый. Нет необходимости во внешнем цикле, все, что вам нужно, это поставить условие и повторить шаги для подготовки CSV.

create or replace procedure dump_table_to_csv
    ( p_tname in varchar2
    , p_dir in varchar2
    )
as
    l_output utl_file.file_type;
    l_theCursor integer default dbms_sql.open_cursor;
    l_columnValue varchar2(4000);
    l_status integer;
    l_query varchar2(1000)
        default 'select * from ' || p_tname;
    l_colCnt number := 0;
    l_separator varchar2(1);
    l_descTbl dbms_sql.desc_tab;
    l_file_seq number := 1;
    l_rows_written number := 0;
    l_nls_format varchar2(200);
begin
    l_output := utl_file.fopen( p_dir, 'test_'||l_file_seq||'_'||to_char(systimestamp, 'YYYYMMDDHH24MISSFF3'), 'w' , 32760); 
    l_file_seq := l_file_seq + 1;
    SELECT value into l_nls_format
      FROM   nls_session_parameters
     WHERE  parameter = 'NLS_DATE_FORMAT';
    execute immediate 'alter session set nls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';

    dbms_sql.parse( l_theCursor, l_query, dbms_sql.native );
    dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );

    for i in 1 .. l_colCnt loop
        utl_file.put( l_output, l_separator || '"' || l_descTbl(i).col_name || '"' );
        dbms_sql.define_column( l_theCursor, i, l_columnValue, 4000 );
        l_separator := ',';
    end loop;

    utl_file.new_line( l_output );

    l_status := dbms_sql.execute(l_theCursor);

    while (dbms_sql.fetch_rows(l_theCursor) > 0 ) loop

        if l_rows_written >= 10000 then
          utl_file.fclose( l_output );
          l_output := utl_file.fopen( p_dir, 'test_'||l_file_seq||'_'||to_char(systimestamp, 'YYYYMMDDHH24MISSFF3'), 'w' , 32760);
          for i in 1 .. l_colCnt loop
              utl_file.put( l_output, l_separator || '"' || l_descTbl(i).col_name || '"' );
              l_separator := ',';
          end loop;
          utl_file.new_line( l_output );

          l_file_seq := l_file_seq + 1;
          l_rows_written := 0;
        end if;

        l_separator := '';

        for i in 1 .. l_colCnt loop
            dbms_sql.column_value( l_theCursor, i, l_columnValue );
            utl_file.put( l_output, l_separator || l_columnValue );
            l_separator := ',';
        end loop;

        utl_file.new_line(l_output );
        l_rows_written := l_rows_written + 1;
    end loop;

    dbms_sql.close_cursor(l_theCursor);
    utl_file.fclose(l_output );

    execute immediate 'alter session set nls_date_format='''||l_nls_format||''' ';
exception
    when others then
        utl_file.fclose( l_output );
        execute immediate 'alter session set nls_date_format='''||l_nls_format||''' ';
        raise;
end;

Возможно, было бы неплохо разгрузить код для подготовки CSV к отдельной (под) процедуре, чтобы избежать дублирования кода. Также имейте в виду, что оригинальная процедура имеет неприятный побочный эффект - переопределение вашего nls_date_format на dd-MON-yy безотносительно к тому, как оно было установлено перед выполнением; Я исправил это.

...