5

我有一个查询将大量数据返回到 CSV 文件中。事实上,Excel 无法打开它——行太多。有没有办法控制spool每次处理 65000 行时假脱机到一个新文件?理想情况下,我希望将输出放在按顺序命名的文件中,例如large_data_1.csv, large_data_2.csv,large_data_3.csv等...

我可以dbms_output在 PL/SQL 块中使用来控制输出的行数,但是我将如何切换文件,因为spoolPL/SQL 块似乎无法访问?

(甲骨文 10g)

更新:

我无权访问服务器,因此将文件写入服务器可能无法正常工作。

更新 2:

某些字段包含自由格式的文本,包括换行符,因此在写入文件后计算换行符并不像在返回数据时计算记录那么容易......

4

6 回答 6

9

有解决方案,不知道为什么我没有早点想到这个......

基本思想是主 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
于 2010-04-13T18:42:37.317 回答
4

试试这个纯 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
于 2010-04-14T07:48:43.950 回答
1

对生成的文件使用拆分。

于 2010-04-13T14:09:04.697 回答
0

utl_file是您正在寻找的包。您可以编写游标并遍历行(将它们写出)以及何时mod(num_rows_written,num_per_file) == 0开始新文件。它在 PL/SQL 块中运行良好。

这是参考utl_file: http: //www.adp-gmbh.ch/ora/plsql/utl_file.html

注意: 我在这里假设可以将文件写入服务器。

于 2010-04-13T14:00:20.973 回答
0

您是否查看过在 Excel 中设置外部数据连接(假设生成的 CSV 文件仅用于在 Excel 中使用)?您可以定义一个限制返回行的 Oracle 视图,并在查询中添加一些参数以允许用户进一步限制结果集。(无论如何,我从来不明白有人对 Excel 中的 64K 行做了什么)。

我觉得这有点像 hack,但您也可以使用 UTL_MAIL 并生成附件以通过电子邮件发送给您的用户。附件的大小限制为 32K,因此您必须跟踪光标循环中的大小并在此基础上开始一个新附件。

于 2010-04-13T15:06:32.570 回答
0

虽然您的问题询问如何将大量数据分解为 Excel 可以处理的块,但我会问 Excel 操作的任何部分是否可以移动到 SQL(PL/SQL?)中,从而减少数据量。最终,它必须被简化才能对任何人都有意义。数据库是一个很好的引擎来完成这项工作。

当您将数据减少到更可展示的数量甚至最终结果时,将其转储到 Excel 以进行最终展示。

这不是您要寻找的答案,但我认为在完成工作变得困难时询问您是否使用了正确的工具总是很好的。

于 2012-05-07T19:49:51.427 回答