Generate a native Excel file with SQLX startup 594127 1280

Generate a native Excel file with SQLX

I wanted to generate an Excel file with data coming from an Oracle database. One of the best ways to do this is to generate an Microsoft Excel XML file. Starting from Office 2003 this XML format is supported. This way you are able to generate a native Excel file in stead of a CSV file.

In a previous post I explained how SQLX can be used to generate XML. With SQLX I was able to generate the Excel XML with only one query. The input data (Excel cell data) is supplied into the query by using a table of objects. This table of objects can be instantiated and filled by any query that supplies the data that you want to have in your Excel file. The advantage of this table of objects is that the Excel data is provided by a parameter making the query generic.


— Object holding cell data
CREATE OR REPLACE
TYPE xls_cell AS OBJECT
( cell_col  NUMBER(8)
,cell_row  number(8)
,cell_Type varchar2(60)
,cell_value VARCHAR2(2000)
,cell_style VARCHAR2(10)
)

— Table of cells
CREATE OR REPLACE
TYPE xls_cells AS TABLE OF xls_cell ;

The following example fills the table with 3 rows and 5 columns:

function test
return  xls_cells
is
  l_cells xls_cells := xls_cells();
begin
  l_cells.extend(15);

  l_cells(1) := xls_cell(1,1,'String','test11','s23');
  l_cells(2) := xls_cell(2,1,'String','test12','s23');
  l_cells(3) := xls_cell(3,1,'String','test13','s23');
  l_cells(4) := xls_cell(4,1,'String','test14','s23');
  l_cells(5) := xls_cell(5,1,'String','test15','s23');

  l_cells(6) := xls_cell(1,2,'String','test21','s22');
  l_cells(7) := xls_cell(2,2,'String','test22','s22');
  l_cells(8) := xls_cell(3,2,'String','test23','s22');
  l_cells(9) := xls_cell(4,2,'String','test24','s22');
  l_cells(10) := xls_cell(5,2,'String','test25','s22');

  l_cells(11) := xls_cell(1,3,'String','test31','s22');
  l_cells(12) := xls_cell(2,3,'String','test32','s22');
  l_cells(13) := xls_cell(3,3,'String','test33','s22');
  l_cells(14) := xls_cell(4,3,'String','test34','s22');
  l_cells(15) := xls_cell(5,3,'String','test35','s22');

  return l_cells;
end;

The following query generates a CLOB holding the Excel XML data. The input data is provided in the p_data parameter. This parameter is of type xls_cells. The p_worksheetname is a varchar2 input parameter holding the name of the Excel sheet.

with row_counter -- Select the number of rows
as   (select distinct cell_row as cell_rows
    from table(cast(p_data as xls_cells)) -- Cast the table of object to a table, usable in a query
    )
,    col_counter -- Select the number of columns
as   (select distinct cell_col as cell_cols
    from table(cast(p_data as xls_cells))
    )
select XMLElement("Workbook", XMLAttributes('http://www.w3.org/TR/REC-html40' AS "xmlns:html"
                                            ,'urn:schemas-microsoft-com:office:spreadsheet' AS "xmlns:ss"
                                            ,'urn:schemas-microsoft-com:office:excel' AS "xmlns:x"
                                            ,'urn:schemas-microsoft-com:office:office' AS "xmlns:o"
                                            ,'urn:schemas-microsoft-com:office:spreadsheet' AS "xmlns"
                                            )
                  , XMLElement("Styles"
                    , XMLElement( "Style", XMLAttributes( 'Normal' AS "ss:Name", 'Default' AS "ss:ID") -- Generate a style
                                , XMLElement("Alignment", XMLAttributes ('Bottom' AS "ss:Vertical"))
                                , XMLElement("Borders")
                                , XMLElement("Font")
                                , XMLElement("Interior")
                                , XMLElement("NumberFormat")
                                )
                    , XMLElement( "Style", XMLAttributes( 's23' AS "ss:ID")                            -- Generate a style
                                , XMLElement("Alignment")
                                , XMLElement("Borders")
                                , XMLElement("Font", XMLAttributes ('1' AS "ss:Bold",'Swiss' AS "x:Family"))
                                , XMLElement("Interior", XMLAttributes ('Solid' AS "ss:Pattern",'#C0C0C0' AS "ss:Color"))
                                , XMLElement("NumberFormat")
                                )
                    , XMLElement( "Style", XMLAttributes( 's22' AS "ss:ID")                            -- Generate a style
                                , XMLElement("Alignment")
                                , XMLElement("Borders")
                                , XMLElement("Font")
                                , XMLElement("Interior")
                                , XMLElement("NumberFormat", XMLAttributes ('0' AS "ss:Format"))
                                )
                  )
                  , XMLElement( "Worksheet", XMLAttributes( p_worksheetname as "ss:Name")
                    , XMLElement( "Table"
                      , ( select XMLAgg( XMLElement( "Column", XMLAttributes( '20' as "ss:Width")) -- Predefine the columns
                                        )
                          from   col_counter
                        )
                      , ( select XMLAgg( XMLElement( "Row"                                         -- Generate a row
                                                , ( select XMLagg(XMLElement( "Cell", XMLAttributes( cell_style as "ss:StyleID") -- Generate a Cell
                                                                            , XMLElement("Data", XMLAttributes( cell_type as "ss:Type"), cell_value
                                                                              )
                                                                            )
                                                                  )
                                                    from   table(cast(p_data as xls_cells))
                                                    where  cell_row = row_counter.cell_rows        -- Make sure the cells are in the correct row
                                                  )
                                                )
                                    )
                          from  row_counter
                        )
                      )
                    )
                  ).getclobval()
into l_xls
from dual;

A drawback of this query is that it only generates one sheet. But this could of course by easily added to this query.

8 Comments

  1. Marcus April 22, 2008
  2. Marcus April 22, 2008
  3. John March 6, 2008
  4. Jason Bennett July 5, 2007
  5. Mahesh March 28, 2007
  6. Marc January 27, 2006
  7. Marc January 27, 2006
  8. karl January 21, 2006