Create an Excel-file with PL/SQL
For this project I took an Apex-plugin I have written, (IR) Report to Excel (xlsx), and turned it into a PL/SQL package. With this package it’s very easy to create an Excel 2007 file with only a few lines of PL/SQL code.
begin as_xlsx.query2sheet( 'select * from dual' ); as_xlsx.save( 'MY_DIR', 'my.xlsx' ); end;
The main purpose for this package is getting data from the database into an Excel file, so I deliberate did not include some Excel functionality, such as formulas, into the package. Excel itself is a far better tool for such things.
Anton
The source code for the package: as_xlsx
P.S. I have added the possibility to add Comments and MergedCells to the Excel-file
P.S.2 And bold/italic fonts
P.S.3 Fixed issue with timezones with a regionname
P.S.4 Fixed issue with XML-escaping from text
P.S.5 Fixed NLS-issue with column width
P.S.6 Added p_rgb to get_font
P.S.7 Fixed bug in add_string
P.S.8 Fixed set_autofilter (only one autofilter per sheet, added _xlnm._FilterDatabase)
Added list_validation = drop-down
Related posts:
This entry was posted by Anton Scheffer on February 19, 2011 at 4:24 pm, and is filed under Devel. + PL/SQL tools, General. Follow any responses to this post through RSS 2.0.You can skip to the end and leave a response. Pinging is currently not allowed.
-
Leave a Reply Cancel reply
-
#4 written by James Van Zandweghe 1 year ago
Hello,
Great package and functionality but i think i found a possible bug in the query2sheet function of the package.
In that function you have the t_sheet pls_integer variable.
you use that variable before a value is assigned to it.
Because of this you can’t specify what sheet you want to use for the results.
I think its better you put the following code at the beginning of your function
beginÂ
if p_sheet is null then
t_sheet := new_sheet;
else
t_sheet := nvl(p_sheet, workbook.sheets.count());
end if;
As you can see i also needed to overlead the new_sheet so that it returns the index of the new sheet.
Â-
#5 written by Anton Scheffer 1 year ago
-
-
#8 written by dieterdaniel 1 year ago
Understandable, it’s not the “native” Excel format that PL/SQL is working with.
I keep playing around with it. As I’m just starting out more examples would be aprecciated if other people already developed more “fancy” code to generate XLS sheets. Especially putting formulas or stuff like graphs into the results. -
#9 written by dieterdaniel 1 year ago
-
#11 written by TonyReed 1 year ago
Finally a litle more time to experiment.
I have modified it to the following:
<workbookPr date1904=”false” dateCompatibility=”true” defaultThemeVersion=”124226″/>Dates seem to be displaying correctly in Excel/win and Excel/Mac.
Dates are displaying as their decimal value in openOffice, but I can live with that, at least for now.
-
#12 written by TonyReed 1 year ago
Got it!
Had a look at the openOffice preferences. Seems that openOffice allows three seperate start dates as the basis for calculating the decimal date.
- 12/30/1899
- 01/01/1900 (StarCalc 1.0)
- 01/01/1904
The last of these options was checked.
I noticed that the as_xlsx package used the following formular for date cell contents:
(p_value – to_date(’01-01-1900′,’DD-MM-YYYY’)Changing the openOffice preference to 01/01/1900 yielded immediate results, the dates which previously were years off are now correct.
Googling “xlsx date format 1900 1904″ yields a lot of results, the main difference seems to be between mac/win. I’m on a Mac which may be why the default was 1904.
I wonder if there is a way of specifying the date initalisation point in the actual xlsx file.
-
#15 written by TonyReed 1 year ago
There seems to be a nls issue as well, possibly related.
modified the query in the above procedure to:
select sysdate, to_char(sysdate,”DD/MM/YYYY HH24:Mi”) to_charSysdate from dualResult is:
SYSDATEÂ Â Â Â Â Â Â Â Â Â Â Â TO_CHARSYSDATE
40881.6090856482Â Â Â 06/12/2011 14:37
converted decimal to date:
SYSDATEÂ Â Â Â Â Â Â Â Â Â Â Â Â Â TO_CHARSYSDATE
12/05/2015 14:37:05Â Â Â 06/12/2011 14:37Somethings definitely rotten in the state of Norway.
Â-
#16 written by Anton Scheffer 1 year ago
-
-
#17 written by TonyReed 1 year ago
Hi Anton,
Looks like this might be a specific problem with openOffice. I just tested with a copy of office 2010 – works nicely.
In openOffice the following procedure returns:
SYSDATE
40881.4262384259
Formatting the decimal as a date turns it into:
12/05/2015 10:13:47
Â
procedure test_xlsx is
v_xlsx blob;
v_mime_type varchar2(255):=’application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’;
begin
as_xlsx.query2sheet(p_query=> ‘select sysdate from dual’);
v_xlsx:=as_xlsx.finish;
owa_util.mime_header(CCONTENT_TYPE=>v_mime_type, BCLOSE_HEADER=>false, CCHARSET=>’UTF-8′);
htp.p(‘Expires: ‘||to_char(sysdate, ‘Dy, DD Mon YYYY HH24:Mi:ss’)||’ GMT’);
htp.p(‘Cache-Control: no-cache’);
htp.p( ‘Content-length: ‘ || length(v_xlsx) );
htp.p( ‘Content-Disposition: filename=”‘ || ‘test-’||to_char(sysdate,’YYYYMMDDHH24miss’)||’.xlsx’ || ‘”‘ );
owa_util.HTTP_HEADER_CLOSE;
wpg_docload.download_file( v_xlsx );
end;
The procedure can be run at:
https://production.presstogo.com/mars/xxtest.test_xlsxAny ideas what the issue is?
-
#19 written by TonyReed 1 year ago
Hi Anton & co,
I’m having trouble with dates.
The files return a cryptic decimal value for all date fields.
e.g. 40734.7118171296When manually formatting the cell to a date this is displayed as:
07/11/2015 17:05:01
Database parameters are:NLS_DATE_FORMATÂ Â Â Â Â Â Â Â Â Â DD-MON-RR
NLS_DATE_LANGUAGEÂ Â Â Â Â Â Â Â AMERICAN
NLS_CHARACTERSETÂ Â Â Â Â Â Â Â Â AL32UTF8hope you can point me in the right direction.
-
Hi, I’m back

Some time ago I realized that 11.1 has problems with “utl_compress.lz_compress”, in fact, does not compress at all, therefore large sheets can be really big. This is replacement:
create or replace and compile java source named zip_blob as
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.Deflater;
import java.sql.Blob;
public class zip_blob
{
public zip_blob()
{
}
public static int zip_blob_compressed_out(java.sql.Blob in_blob,java.sql.Blob[] out_blob, float compression)
{
final int comp = (int)compression;
try{
//zipoutputstream from out_blob
Blob zip_out = out_blob[0];
OutputStream os = zip_out.setBinaryStream(1);
GZIPOutputStream zos = new GZIPOutputStream(os)Â Â Â {
{
def.setLevel(comp);
}
};
int chunksize = 32768;
//add data to zip
long len = in_blob.length();
long offset = 1;
byte[] buffer;
while (offset < len) {
buffer = in_blob.getBytes(offset, chunksize);
if (buffer == null)
break;
zos.write(buffer, 0, buffer.length);
offset += buffer.length;
}
zos.close();
out_blob[0] = zip_out;Â Â Â Â Â Â Â
}catch (Exception e) {
e.printStackTrace(System.out);
return 1;
}
return 0;
}Â
}
CREATE OR REPLACE PACKAGE zip_blob ASFUNCTION zip_blob_compress(in_blob IN BLOB, out_blob in out nocopy BLOB, comp number)Â RETURN number
AS LANGUAGE JAVA
NAME ‘zip_blob.zip_blob_compressed_out(java.sql.Blob, java.sql.Blob[], float) return java.lang.int’;END zip_blob;
Commenting out:
t_blob := utl_compress.lz_compress( p_content);
and adding:
t_zip_res integer;
….
t_zip_res := zip_blob.zip_blob_compress(p_content,t_blob,9);
plus
do the trick …Cheers
-
#22 written by Amit 1 year ago
Hi ,
I got following error  while compiling the package.Compilation errors for PACKAGE BODY NANO.AS_XLSX
Â
Error: PLS-00201: identifier ‘UTL_FILE’ must be declared
Line: 95
Text: T_FH Â UTL_FILE.FILE_TYPE;
Â
Error: PL/SQL: Item ignored
Line: 95
Text: T_FH Â UTL_FILE.FILE_TYPE;
Â
Error: PLS-00320: the declaration of the type of this expression is incomplete or malformed
Line: 98
Text: T_FH := UTL_FILE.FOPEN(P_DIRECTORY, P_FILENAME, ‘wb’);
Â
Error: PL/SQL: Statement ignored
Line: 98
Text: T_FH := UTL_FILE.FOPEN(P_DIRECTORY, P_FILENAME, ‘wb’);
Â
Error: PLS-00320: the declaration of the type of this expression is incomplete or malformed
Line: 100
Text: UTL_FILE.PUT_RAW(T_FH, DBMS_LOB.SUBSTR(P_BLOB, T_LEN, I * T_LEN + 1));
Â
Error: PL/SQL: Statement ignored
Line: 100
Text: UTL_FILE.PUT_RAW(T_FH, DBMS_LOB.SUBSTR(P_BLOB, T_LEN, I * T_LEN + 1));
Â
Error: PLS-00320: the declaration of the type of this expression is incomplete or malformed
Line: 102
Text: UTL_FILE.FCLOSE(T_FH);
Â
Error: PL/SQL: Statement ignored
Line: 102
Text: UTL_FILE.FCLOSE(T_FH);
Â
Error: Hint: Value assigned to ‘T_IND’ never used in ‘NEW_SHEET’
Line: 299
Text: T_IND := GET_FONT(‘Calibri’);
Â
Error: Hint: Value assigned to ‘T_IND’ never used in ‘NEW_SHEET’
Line: 302
Text: T_IND := GET_FILL(‘none’);
Â
Error: Hint: Value assigned to ‘T_IND’ never used in ‘NEW_SHEET’
Line: 303
Text: T_IND := GET_FILL(‘gray125′);
Â
Error: Hint: Value assigned to ‘T_IND’ never used in ‘NEW_SHEET’
Line: 306
Text: T_IND := GET_BORDER(”, ”, ”, ”);
Â
Error: Hint: Variable ‘TS’ is declared but never used in ‘FINISH’
Line: 833
Text: TS Â Â Â Â TIMESTAMP := SYSTIMESTAMP;
Can you please tell me how to fix it?Thnx
-
#25 written by Peter 1 year ago
-
#27 written by Peter 1 year ago
-
#29 written by Peter 1 year ago
-
Anton, Further to Morten’s “refcursor2sheet” request.
Could you refactor query2sheet into a separate “query2sheet” and “cursor2sheet”. The former would do the open cursor/parse then pass the opened cursor to the latter (which would do the describe columns onwards).
In 11g we can covert the refcursor to a DBMS_SQL cursor and call “cursor2sheet” directly.
I’ve put those changes in on:
http://www.sydoracle.com/Codespace/as_xlsx9.txt
 -
I’v tried that solution but it relay mess up with shared strings mapping in workbook when you have null columns between not null. Additionally it generates a lot of redundant cell data with same shared string that is mapped to null. Generally I think better way is to stop generation of null cells at all, since you got cell reference like ‘A1′ etc in sheet.xml exel can handle this and all xlsx is smaller
-
damn tags
when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 )
then
dbms_sql.column_value( t_c, 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;added: if v_tab( i + v_tab.first() ) is not null then … end if;
-
-
#43 written by prunoki 1 year ago
-
#45 written by Vivian 1 year ago
-
#46 written by Anton Scheffer 1 year ago
I prefer comments in Dutch, but German or English are OK to, but Spanish no

Do you mean procedure new_sheet( p_sheetname varchar2 := null );???begin
as_xlsx.new_sheet( ‘query 1′ );
as_xlsx.query2sheet( ‘select 1 r from dual’, p_sheet => 1 );
as_xlsx.new_sheet( ‘query 2′ );
as_xlsx.query2sheet( ‘select 2 r from dual’, true, ‘MY_DIR’, ‘my.xlsx’, 2 );
end;
-
-
#47 written by Partha Sarathi Paul 1 year ago
Hi Anton,
It’s really very nice thing. you just save a lot of time for us. But one thing I have found that while I’m trying to create a large excel file , suppose it has 4000 rows 470 coulms (i.e. A-RB) it takes 12 minutes to create the file. Could the time be reduced?
Please help.
Thanks you once more for your great endeavour.
-
#49 written by Dirk Leiendorf 1 year ago
Hi Anton,
really great work!
Correct me if I’m wrong, but after taking a quick look at your code it seems you put every string as new string in the SharedStrings.xml. A while ago just out of curiosity I took a closer look at the Office Open XML file format. I remember that one of the reasons MS gave for writing strings to a different file was that in spreadsheets ”highly repetitive strings are very common”. So it should be fairly easy to greatly reduce the file size of the resulting xlsx file by only adding new strings to your tp_strings and just reference it for duplicates.
Don’t forget to differentiate between count and uniqueCount in your sharedStrings.xml
Cheers -
-
#54 written by Peter 1 year ago
Hi Anton,
great! we tried to implement it by our self. then in stumbled across you package. great!
but very soon we’ve got  an issue:
2 out of 21 Columns do not appear when opening the xlsx file in excel. they are marked as hidden. to show them youhave to  expand them manually. both colums are filled with date values and are empty (null) in the first couple of rows.
using excel i saved the xlsx as xml, without any changes and found this section “<Worksheet ss:Name=”Sheet1″>
<Table ss:ExpandedColumnCount=”24″ ss:ExpandedRowCount=”1563″ x:FullColumns=”1″
x:FullRows=”1″ ss:DefaultRowHeight=”15″>
<Column ss:Index=”14″ ss:Hidden=”1″ ss:AutoFitWidth=”0″/>
<Column ss:Index=”21″ ss:Hidden=”1″ ss:AutoFitWidth=”0″/>”
in the very beginning of the xml file.
any idea?
grtz
Peter -
#56 written by stevenquave 1 year ago
I also tried other fonts like Arial, Times New Roman but still cannot work….
My Oracle is installed in the remote server(unix without excel). After generating excel file, I will get the excel file to my PC. I have no idea whether those fonts were in the server or not but can make suer my pc can display those fonts well?
Do you mean I should install those fonts in my server? Is that in the Oracle packages?Â
 Â-
#57 written by Anton Scheffer 1 year ago
-
-
#59 written by Stevenquave 1 year ago
Your sample code is quite useful ..  Before that, I used .Net to make excel file, but it took much of my time waiting for generating files through connecting database and file formatting.Â
Tried to set the font name as the code below, but the font name cannot display well and always show as the default font setting of my pc.
Do you have any suggestions about that? Look forward for your reply and thanks in advance.cell( c, t_cur_row + i, v_tab( i + v_tab.first() ), p_sheet => t_sheet,p_borderId => as_xlsx.get_border( p_bolder, p_bolder, p_bolder, p_bolder),p_fontId=>as_xlsx.get_font(‘Calibri’,p_fontsize=>8));
Stevenquave
-
#61 written by dpigetyphie 2 years ago
-
#63 written by Björn 2 years ago
@Morton: You should be able to solve your SYS_REFCURSOR problem with this function:
DBMS_SQL.TO_CURSOR_NUMBER
http://download.oracle.com/docs/cd/E11882_01/appdev.112/e16760/d_sql.htm#CHDJDGDG
As fare as I know its only available in 11g -
#64 written by Björn 2 years ago
Hi Anton,
really great package. I only found one little problem when using time zones. If you execute this example:
begin
EXECUTE IMMEDIATE ‘ALTER SESSION SET TIME_ZONE =”Australia/Sydney”’;
as_xlsx.query2sheet( ‘select rownum, x.*
, case when mod( rownum, 2 ) = 0 then rownum * 3 end demo
, case when mod( rownum, 2 ) = 1 then ”demo ” || rownum end demo2 from dual x connect by rownum <= 5′ );
as_xlsx.save( ‘MY_DIR’, ‘my.xlsx’ );
end;
You get an invalid file. I suggest to use this code instead:
<dcterms:created xsi:type=”dcterms:W3CDTF”>’ || to_char( current_timestamp, ‘yyyy-mm-dd”T”hh24:mi:ssTZH:TZM’ ) || ‘</dcterms:created>
<dcterms:modified xsi:type=”dcterms:W3CDTF”>’ || to_char( current_timestamp, ‘yyyy-mm-dd”T”hh24:mi:ssTZH:TZM’ ) || ‘</dcterms:modified> -
#67 written by Anton Scheffer 2 years ago
-
#70 written by Achim Rosenfeld 2 years ago
Thank you for this post, Anton. You are saving me the trouble to figure out the new excel xml structure on my own. I did it once with the former 2003 spreadsheetml…
While playing around with it I came across two problems:
- our database language is set to German, thus decimals are represented by ‘,’ instead of ‘.’ That messes up alle non-integer number cells and date cells. When I set the nls_language to american it works.
- the autofilter function is missing a <definedNames> tag in the workbook.xml file. so the autofilter will be rendered but doesn’t work. I added the following code between the </sheets> and the <calcPr> tags
bFirst := true;
for s in 1 .. workbook.sheets.count() LOOP
if workbook.sheets( s ).autofilters.count() > 0 THEN
if bFirst then
t_xxx := t_xxx || ‘
<definedNames>’;
end if;t_col_min := 1;
t_col_max := 1;
t_row_ind := workbook.sheets( s ).rows.first();while t_row_ind is not null loop
t_col_min := least( t_col_min, workbook.sheets( s ).rows( t_row_ind ).first() );
t_col_max := greatest( t_col_max, workbook.sheets( s ).rows( t_row_ind ).last() );
t_row_ind := workbook.sheets( s ).rows.next( t_row_ind );
end loop;for a in 1 .. workbook.sheets( s ).autofilters.count() loop
t_xxx := t_xxx || ‘
<definedName name=”_xlnm._FilterDatabase” localSheetId=”‘ || (s – 1) || ‘” hidden=”1″>Sheet’ || s || ‘!$’ ||
fk_alfan_col( nvl( workbook.sheets( s ).autofilters( a ).column_start, t_col_min ) ) || ‘$’ ||
nvl( workbook.sheets( s ).autofilters( a ).row_start, workbook.sheets( s ).rows.first() ) || ‘:$’ ||
fk_alfan_col( nvl( workbook.sheets( s ).autofilters( a ).column_end, t_col_max ) ) || ‘$’ ||
nvl( workbook.sheets( s ).autofilters( a ).row_end, workbook.sheets( s ).rows.last() ) ||
‘</definedName>’;
end loop;if bFirst then
t_xxx := t_xxx || ‘
</definedNames>’;
bFirst := false;
end if;
end if;
end loop;-
#71 written by Anton Scheffer 2 years ago
@Vasya: You can’t merge cells, like I said in the blog I build this package for unloading data, not to create fancy spreadsheets.
@Achim: You are right about the NLS-settings, I will fix that some day. But Autofilters work for me without definedNames, at least with Excel 2010. I will test it with Excel 2007.
-
-
-
#77 written by Anton Scheffer 2 years ago
@Morten. In this case it’s a bit different, to read a (unknown) refcursor every column is turned into a string, even numbers and dates. And its (very) difficult to detect a number or date and show these in the right way in the Excel file. But feel free to do it yourself, and I’m happy to include it in the official version
-
#78 written by Anon 2 years ago
Very nice and very useful, Anton! Thanks for sharing.
I have a small enhancement request: Can you add a “refcursor2sheet” procedure? It should work along the same lines as “refcursor2pdf” from the example to your PDF package. I could add it myself, but I’m sure others would find this useful as well, so it’s perhaps better if you add it to the “official” version of your package.
- Morten - Comment Feed for this Post
- Read an Excel xlsx with PL/SQL
- The Very Very Latest in Database Development – slides from the Expertezed presentation
- The APEX of Business Value… or: the Business Value of APEX? Cloud takes Oracle APEX to new heights!
- Kom kennismaken met AMIS en doe mee met uitdagende projecten
- OOW 2012: The Very Very Latest in Database Development (CON4792)
- Oracle RDBMS 10GR1: solution to avoid character encoding in XML with UPDATEXML
- Reduce occurrence of ORA-04068 while upgrading PL/SQL packages by moving global variables to Application Context
- Valuable Java, JavaScript and ADF resources
- Select a blob across a database link, without getting ORA-22992
- ADF 11g Business components – Create PL/SQL based entities
Hi Anton,
thanks a lot for this very usefull package.
I took time to add a personnal “Cursor2Sheet” procedure which works also on Oracle 10 (using XMLType in between
I Post it here incase it is of any use to anybody :
/************************************/
procedure cursor2sheet
( p_sql in sys_refcursor
, p_column_headers boolean := true
, p_directory varchar2 := null
, p_filename varchar2 := null
, p_sheet pls_integer := null
, p_footer boolean := true
)
is
ctx dbms_xmlgen.ctxHandle;
tmpXml XMLType ;
cursor cData is
Select
t2.Column_Value.GetRootElement() ColName
, ExtractValue(t2.Column_Value, ‘node()’) Value
From Table(XMLSequence(tmpXml)) t
, Table(XMLSequence(Extract(t.Column_Value,’/ROWSET/ROW/node()’))) t2
Order by RowNum ;
tsColHeaders SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL();
tsValues SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL();
t_sheet pls_integer := 1;
t_cur_row pls_integer := 1;
colId pls_integer := 1;
nColNumber pls_integer;
n pls_integer;
aTmpVal SYS.AnyData ;
nNumVal Number ;
tTSVal TimeStamp ;
dDateVal Date ;
sVarcharVal Varchar2(4000) ;
bGotType boolean ;
eTypeConvert Exception ;
eTypeDateFormat Exception ;
eTypeNonNumeric Exception ;
eTypeNotDefined Exception ;
pragma exception_init(eTypeConvert,-6502);
pragma exception_init(eTypeDateFormat,-1830);
pragma exception_init(eTypeNonNumeric,-1858);
begin
— XML Creation from the sys_refcursor
ctx := dbms_xmlgen.newContext(p_sql);
— this is important in order to get all the column headers, even if all data are null
dbms_xmlgen.SetNullHandling(ctx, dbms_xmlgen.EMPTY_TAG);
dbms_xmlgen.getXMLType(ctx, TmpXml);
if p_sheet is null
then
new_sheet;
end if;
— Load Columns and Values into Arrays
Open cData ;
Fetch cData bulk collect into tsColHeaders, tsValues ;
Close cData ;
— get distinct headers
tsColHeaders := set(tsColHeaders) ;
— get number of headers (of columns)
nColNumber := tsColHeaders.count ;
— Create column headers if wanted
If p_column_headers
Then
— set headers into sheet
For i in tsColHeaders.first .. tsColHeaders.last
Loop
cell(i, t_cur_row, tsColHeaders(i), p_fontId => get_font(‘Calibri’, p_bold => true), p_sheet => t_sheet);
End Loop ;
t_cur_row := 2;
End if;
t_sheet := nvl(p_sheet, workbook.sheets.count());
— fill cells
For i in tsValues.first .. tsValues.last
Loop
— check if we must reset col to 1 and go to next line
If i > nColNumber
and mod(i ,nColNumber) = 1
Then
— reset colId to 1 and go to next line
colId := 1 ;
t_cur_row := t_cur_row + 1;
End If ;
— find the good type and insert into Cell
— initialize “checker”
bGotType := false ;
— Number ?
If Not bGotType
Then
Begin
aTmpVal := Sys.AnyData.ConvertNumber(tsValues(i)) ;
bGotType := true ;
n := aTmpVal.GetNumber(nNumVal) ;
— load data into cell
cell(colId, t_cur_row, nNumVal, p_sheet => t_sheet ) ;
— if conversion fails
Exception
When eTypeConvert or eTypeDateFormat or eTypeNonNumeric
Then
bGotType := false ;
End ;
End If ;
— TimeStamp ?
If Not bGotType
Then
Begin
aTmpVal := Sys.AnyData.ConvertTimestamp(tsValues(i)) ;
bGotType := true ;
n := aTmpVal.GetTimeStamp(tTSVal) ;
— load data into cell
cell(colId, t_cur_row, to_date(tTSVal), p_sheet => t_sheet ) ;
— if conversion fails
Exception
When eTypeConvert or eTypeDateFormat or eTypeNonNumeric
Then
bGotType := false ;
End ;
End If ;
— Date ?
If Not bGotType
Then
Begin
aTmpVal := Sys.AnyData.ConvertDate(tsValues(i)) ;
bGotType := true ;
n := aTmpVal.GetDate(dDateVal) ;
— load data into cell
cell(colId, t_cur_row, dDateVal, p_sheet => t_sheet ) ;
— if conversion fails
Exception
When eTypeConvert or eTypeDateFormat or eTypeNonNumeric
Then
bGotType := false ;
End ;
End If ;
— Varchar2 ?
If Not bGotType
Then
Begin
aTmpVal := Sys.AnyData.ConvertVarchar2(tsValues(i)) ;
bGotType := true ;
n := aTmpVal.GetVarchar2(sVarcharVal) ;
— load data into cell
cell(colId, t_cur_row, sVarcharVal, p_sheet => t_sheet ) ;
— if conversion fails
Exception
When eTypeConvert or eTypeDateFormat or eTypeNonNumeric
Then
bGotType := false ;
End ;
End If ;
— unsupported type
If Not bGotType
Then
raise eTypeNotDefined ;
End If ;
— go to next col
colId := colId + 1 ;
End Loop ;
If p_footer
Then
— set footer
cell(1, t_cur_row+2 , ‘Generated ‘||sysdate||’ by ‘||user, p_sheet => t_sheet ) ;
End If ;
if ( p_directory is not null and p_filename is not null )
then
save( p_directory, p_filename );
end if;
exception
when eTypeNotDefined
then
raise_application_error(-20999,’one data has an unsupported type’, false);
raise;
when others
then
raise_application_error(-20999,’Export to XLSX failed’, true);
end;