SQLPlus - спулинг в несколько файлов из блоков PL / SQL - PullRequest
5 голосов
/ 13 апреля 2010

У меня есть запрос, который возвращает много данных в файл CSV. Фактически, настолько, что Excel не может открыть его - слишком много строк. Есть ли способ управления spool для буферизации в новый файл каждый раз, когда обрабатывается 65000 строк? В идеале я хотел бы, чтобы мой вывод выводился в файлах с последовательным именем, таких как large_data_1.csv, large_data_2.csv, large_data_3.csv и т. Д. *

Я мог бы использовать dbms_output в блоке PL / SQL, чтобы контролировать, сколько строк выводится, но тогда как бы я переключал файлы, поскольку spool, кажется, не доступен из блоков PL / SQL?

(Oracle 10g)

UPDATE:

У меня нет доступа к серверу, поэтому запись файлов на сервер, вероятно, не будет работать.

ОБНОВЛЕНИЕ 2:

Некоторые поля содержат текст произвольной формы, включая разрывы строк, поэтому подсчет разрывов строк ПОСЛЕ записи файла не так прост, как подсчет записей, В то время как данные возвращаются ...

Ответы [ 6 ]

9 голосов
/ 13 апреля 2010

Есть решение, не знаю, почему я не подумал об этом раньше ...

Основная идея заключается в том, что основной сценарий sqplplus создает промежуточный сценарий, который разделит выходные данные на несколько файлов. Выполнение промежуточного сценария приведет к выполнению нескольких запросов с различными диапазонами, наложенными на rownum, и буферизации в отдельный файл для каждого запроса.

set termout off
set serveroutput on
set echo off
set feedback off
variable v_rowCount number;
spool intermediate_file.sql
declare
     i number := 0;
     v_fileNum number := 1;
     v_range_start number := 1;
     v_range_end number := 1;
     k_max_rows constant number := 65536;
begin
    dbms_output.enable(10000);
    select count(*) 
    into :v_err_count
    from ...
    /* You don't need to see the details of the query... */

    while i <= :v_err_count loop

          v_range_start := i+1;
          if v_range_start <= :v_err_count then
            i := i+k_max_rows;
            v_range_end := i;

            dbms_output.put_line('set colsep ,  
set pagesize 0
set trimspool on 
set headsep off
set feedback off
set echo off
set termout off
set linesize 4000
spool large_data_file_'||v_fileNum||'.csv
select data_string
from (select rownum rn, data_object
      from 
      /* Details of query omitted */
     )
where rn >= '||v_range_start||' and rn <= '||v_range_end||';
spool off');
          v_fileNum := v_fileNum +1;
         end if;
    end loop;
end;
/
spool off
prompt     executing intermediate file
@intermediate_file.sql;
set serveroutput off
4 голосов
/ 14 апреля 2010

Попробуйте это для чистого решения SQL * Plus ...

set pagesize 0
set trimspool on  
set headsep off 
set feedback off
set echo off 
set verify off
set timing off
set linesize 4000

DEFINE rows_per_file = 50


-- Create an sql file that will create the individual result files
SET DEFINE OFF

SPOOL c:\temp\generate_one.sql

PROMPT COLUMN which_dynamic NEW_VALUE dynamic_filename
PROMPT

PROMPT SELECT 'c:\temp\run_#'||TO_CHAR( &1, 'fm000' )||'_result.txt' which_dynamic FROM dual
PROMPT /

PROMPT SPOOL &dynamic_filename

PROMPT SELECT *
PROMPT   FROM ( SELECT a.*, rownum rnum
PROMPT            FROM ( SELECT object_id FROM all_objects ORDER BY object_id ) a
PROMPT           WHERE rownum <= ( &2 * 50 ) )
PROMPT  WHERE rnum >= ( ( &3 - 1 ) * 50 ) + 1
PROMPT /

PROMPT SPOOL OFF

SPOOL OFF

SET DEFINE &


-- Define variable to hold number of rows
-- returned by the query
COLUMN num_rows NEW_VALUE v_num_rows

-- Find out how many rows there are to be
SELECT COUNT(*) num_rows
  FROM ( SELECT LEVEL num_files FROM dual CONNECT BY LEVEL <= 120 );


-- Create a master file with the correct number of sql files
SPOOL c:\temp\run_all.sql

SELECT '@c:\temp\generate_one.sql '||TO_CHAR( num_files )
                                   ||' '||TO_CHAR( num_files )
                                   ||' '||TO_CHAR( num_files ) file_name
  FROM ( SELECT LEVEL num_files 
           FROM dual 
        CONNECT BY LEVEL <= CEIL( &v_num_rows / &rows_per_file ) )
/

SPOOL OFF

-- Now run them all
@c:\temp\run_all.sql
1 голос
/ 13 апреля 2010

Используйте split в результирующем файле.

0 голосов
/ 07 мая 2012

Хотя ваш вопрос состоит в том, как разбить большой объем данных на куски, которые может обрабатывать Excel, я бы спросил, есть ли какая-либо часть операции Excel, которая может быть перемещена в SQL (PL / SQL?), Которая может уменьшить объем данных. В конечном итоге это должно быть уменьшено, чтобы стать значимым для всех. База данных - отличный движок для этой работы.

Когда вы уменьшите данные до более презентабельных томов или даже до окончательных результатов, создайте дамп для Excel, чтобы сделать окончательную презентацию.

Это не тот ответ, который вы искали, но я думаю, что всегда хорошо спросить, используете ли вы правильный инструмент, когда становится трудно выполнить работу.

0 голосов
/ 13 апреля 2010

Вы смотрели на настройку подключения к внешним данным в Excel (при условии, что файлы CSV создаются только для использования в Excel)? Вы можете определить представление Oracle, которое ограничивает возвращаемые строки, а также добавить некоторые параметры в запрос, чтобы позволить пользователю дополнительно ограничить набор результатов. (Во всяком случае, я никогда не понимал, что кто-то делает со строками 64 КБ в Excel).

Я чувствую, что это что-то вроде хака, но вы также можете использовать UTL_MAIL и создавать вложения в сообщения электронной почты для ваших пользователей. Размер вложений ограничен 32 КБ, поэтому вам придется отслеживать размер в цикле курсора и запускать новое вложение на этой основе.

0 голосов
/ 13 апреля 2010

utl_file - это пакет, который вы ищете. Вы можете написать курсор и перебрать строки (записать их), а когда mod(num_rows_written,num_per_file) == 0, пришло время начать новый файл. Он отлично работает в блоках PL / SQL.

Вот ссылка для utl_file: http://www.adp -gmbh.ch / ора / PLSQL / utl_file.html

Примечание: Здесь я предполагаю, что можно записывать файлы на сервер.

...