使用PL / SQL创build一个Excel文件(.xlsx)

关于我以前的问题返回显式游标的SQL语句 ,我能够使用DBMS_SQLUTL_FILE生成Excel (.xls)文件并传递SYS_REFCURSOR (代码在上一个问题中)。 然而,我遇到的一个挑战是生成的文件超过25 MB。 我将通过电子邮件发送给商业用户,它肯定会填满他们的邮箱大小。 当我使用SQL Developer手动提取到xlsx它会生成大约4 MB的数据。

为了解决这个问题,是否可以通过PL/SQL来完成以下工作?

  1. 使用更高版本的Excel( .xlsx )生成文件以压缩大小
  2. 在传输之前压缩.xls文件

我也在这里回顾了类似的post,比如在Excel中使用UTL_FILE包在Excel中编写 ,但是正如答案所述,它需要使用Java。 所以它不适用于我。 另一篇文章, 从Oracle数据库创buildExcel电子表格也使用xls 。 所以它也不适用。

有什么想法吗?

Oracle版本:

 Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production PL/SQL Release 11.2.0.4.0 - Production "CORE 11.2.0.4.0 Production" TNS for Solaris: Version 11.2.0.4.0 - Production NLSRTL Version 11.2.0.4.0 - Production 

我看到了一个名为as_xlsx的包,由Anton Scheffer 创build一个包含PL / SQL的Excel文件,解决了我的问题。 我也修改了一下,放入工作表名称,并允许SYS_REFCURSOR作为一个参数,而不是我以前的post( 返回显式游标的SQL语句 )所需的VARCHAR2

我在过程重载的包规范中添加了这个:

 procedure query2sheet ( p_cur IN OUT SYS_REFCURSOR , p_column_headers boolean := true , p_directory varchar2 := null , p_filename varchar2 := null , p_sheet pls_integer := null , p_sheetname varchar2 := null ); 

我在过程重载的包体中添加了这个(注意:行注释是我修改的行):

 procedure query2sheet ( p_cur IN OUT SYS_REFCURSOR , p_column_headers boolean := true , p_directory varchar2 := null , p_filename varchar2 := null , p_sheet pls_integer := null , p_sheetname varchar2 := null ) is t_sheet pls_integer; t_c integer; t_col_cnt integer; t_desc_tab2 dbms_sql.desc_tab2; t_desc_tab dbms_sql.desc_tab; d_tab dbms_sql.date_table; n_tab dbms_sql.number_table; v_tab dbms_sql.varchar2_table; t_bulk_size pls_integer := 200; t_r integer; t_cur_row pls_integer; t_d number; begin -- Changed if p_sheetname is not null then new_sheet(p_sheetname); else new_sheet; end if; -- End of Change --t_c := dbms_sql.open_cursor; --dbms_sql.parse( t_c, p_sql, dbms_sql.native ); t_d := DBMS_SQL.TO_CURSOR_NUMBER(p_cur); --dbms_sql.describe_columns2( t_c, t_col_cnt, t_desc_tab ); dbms_sql.describe_columns( t_d, t_col_cnt, t_desc_tab ); for c in 1 .. t_col_cnt loop if p_column_headers then cell( c, 1, t_desc_tab( c ).col_name, p_sheet => t_sheet ); end if; --dbms_output.put_line( t_desc_tab( c ).col_name || ' ' || t_desc_tab( c ).col_type ); case when t_desc_tab( c ).col_type in ( 2, 100, 101 ) then --dbms_sql.define_array( t_c, c, n_tab, t_bulk_size, 1 ); dbms_sql.define_array( t_d, c, n_tab, t_bulk_size, 1 ); when t_desc_tab( c ).col_type in ( 12, 178, 179, 180, 181 , 231 ) then --dbms_sql.define_array( t_c, c, d_tab, t_bulk_size, 1 ); dbms_sql.define_array( t_d, c, d_tab, t_bulk_size, 1 ); when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 ) then --dbms_sql.define_array( t_c, c, v_tab, t_bulk_size, 1 ); dbms_sql.define_array( t_d, c, v_tab, t_bulk_size, 1 ); else null; end case; end loop; -- t_cur_row := case when p_column_headers then 2 else 1 end; t_sheet := nvl( p_sheet, workbook.sheets.count() ); -- --t_r := dbms_sql.execute( t_c ); loop --t_r := dbms_sql.fetch_rows( t_c ); t_r := dbms_sql.fetch_rows( t_d ); if t_r > 0 then for c in 1 .. t_col_cnt loop case when t_desc_tab( c ).col_type in ( 2, 100, 101 ) then --dbms_sql.column_value( t_c, c, n_tab ); dbms_sql.column_value( t_d, c, n_tab ); for i in 0 .. t_r - 1 loop if n_tab( i + n_tab.first() ) is not null then cell( c, t_cur_row + i, n_tab( i + n_tab.first() ), p_sheet => t_sheet ); end if; end loop; n_tab.delete; when t_desc_tab( c ).col_type in ( 12, 178, 179, 180, 181 , 231 ) then --dbms_sql.column_value( t_c, c, d_tab ); dbms_sql.column_value( t_d, c, d_tab ); for i in 0 .. t_r - 1 loop if d_tab( i + d_tab.first() ) is not null then cell( c, t_cur_row + i, d_tab( i + d_tab.first() ), p_sheet => t_sheet ); end if; end loop; d_tab.delete; when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 ) then --dbms_sql.column_value( t_c, c, v_tab ); dbms_sql.column_value( t_d, c, v_tab ); for i in 0 .. t_r - 1 loop if v_tab( i + v_tab.first() ) is not null then cell( c, t_cur_row + i, v_tab( i + v_tab.first() ), p_sheet => t_sheet ); end if; end loop; v_tab.delete; else null; end case; end loop; end if; exit when t_r != t_bulk_size; t_cur_row := t_cur_row + t_r; end loop; --dbms_sql.close_cursor( t_c ); dbms_sql.close_cursor( t_d ); if ( p_directory is not null and p_filename is not null ) then save( p_directory, p_filename ); end if; exception when others then --if dbms_sql.is_open( t_c ) if dbms_sql.is_open( t_d ) then --dbms_sql.close_cursor( t_c ); dbms_sql.close_cursor( t_d ); end if; end query2sheet; 

这是创build文件的并发请求中的示例块:

 Procedure EMP_ROSTER_REPORT (p_empno per_all_people_f.employee_number%type , p_bg_id per_business_groups.business_group_id%type , p_email_add per_all_people_f.email_address%type) is l_fh UTL_FILE.FILE_TYPE; l_directory VARCHAR2(30) := 'EXT_TAB_DATA'; l_filename VARCHAR2(100); emp_cur SYS_REFCURSOR; l_message varchar2(100); g_stage varchar2(100); g_zipped_blob blob; cursor p_payroll_cur is select payroll_id , payroll_name , business_group_id from pay_all_payrolls_f where business_group_id = p_bg_id; BEGIN ----------------------------------- g_stage := 'setting the filename'; ----------------------------------- l_filename := 'EMPLOYEE_ROSTER_REPORT_'||TO_CHAR(SYSDATE, 'DD-MON-YYYY-HHMISS'); ------------------------------------------ g_stage := 'Assigning Emp SysRefCursor'; ------------------------------------------ for i in p_payroll_cur loop OPEN emp_cur FOR SELECT 'extra long query here with parameters` from table_a where payroll_id = i.payroll_id; ---------------------------------------------------------- g_stage := 'open Employee Cursor and write into the File'; ---------------------------------------------------------- as_xlsx.query2sheet( p_cur => emp_cur -- Uses Sys_RefCursor Instead of Dynamic SQL (Varchar2) , p_sheetname => i.payroll_name); -- This is where we assign the Sheet Names as_xlsx.freeze_pane( 1,1 ); -- Freeze the topmost and rightmost pane in the Excel Sheet end loop; ------------------------------ g_stage := 'Create the File'; ------------------------------ as_xlsx.save( l_directory , l_filename||'.xlsx'); END EMP_ROSTER_REPORT; 

希望这可以帮助别人! 🙂

Oracle数据库中支持xlsxlsx文件。

你可以做的是创buildCSV(逗号分隔)文件,然后使用使用java.util.zip的自定义java存储过程或使用UTL_COMPRESS的PL / SQL过程将其压缩到压缩文件中。

如果您需要生成XLSX文件并将其发送给业务用户,那么听起来就像是针对DB之外的某些服务的工作。 你应该准备程序,返回ref cursor的报告内容,然后设置一些服务,使用数据生成PDF或XLSX并发送它。

如果你不能在DB之外做,你仍然可以在Oracle内部使用Java。 您可以创build将创buildXLSX的Java过程。 这里是Java创buildExcel文件的一个例子。 但是创build复杂的Java程序并不是最好的解决scheme,可能需要你在DB服务器上安装一些jar文件,所以我将创build一个程序,它将数据和小程序创build文件并将其发送到DB之外。