OraQA

Oracle Question and Answer

  • Do you have a solution to a problem? Do you have an unanswered question? Login and share it with the Oracle community. More...

Oracle News


Entries RSS feed

Comments RSS feed

How to transform a RefCursor query resultset into HTML table in SQL

March 6th, 2012 By Frank Zhou

The following SQL pattern can be used to tranform a RefCursor query resultset into data in HTML table format by utilizing the XSLT functionality. The query can handle NULL in the data columns. The SQL input string should be based on the following format:

SELECT Col AS name_x, Function() AS name_y, Analytical_function( ) AS name_z FROM ( ***ANYTHING*** )

You can change your query into this required format by wrapping your sql inside an inline view if needed. The “AS” keyword and column_name “alias” are required for building the XML tags.


CREATE TABLE PERSON
(ID            NUMBER,
 FIRSTNAME     VARCHAR2(20 BYTE),
 MIDDLENAME    VARCHAR2(20 BYTE),
 LASTNAME      VARCHAR2(20 BYTE),
 LOCATION_ID   NUMBER);

INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME, LOCATION_ID)
VALUES (1, 'Frank', null, 'Zhou', 1);
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME, LOCATION_ID)
VALUES (2, 'Peter', 'B',  'Lee', 1);
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME, LOCATION_ID)
VALUES (3, 'John' , 'C',   'Adam', 2);
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME, LOCATION_ID)
VALUES (4, 'Dave' , null, 'Washington', 2);

CREATE TABLE LOCATION
(LOCATION_ID       NUMBER,
 LOCATION_NAME     VARCHAR2(20 BYTE)
 );

INSERT INTO LOCATION (LOCATION_ID, LOCATION_NAME) 
VALUES (1, 'BOSTON');
INSERT INTO LOCATION (LOCATION_ID, LOCATION_NAME) 
VALUES (2, 'NEW YORK');
COMMIT;

set long 10000;
variable sql_input varchar2(500)
BEGIN
   :sql_input:= q'[
     SELECT b.location_name AS location,
            COUNT (*) AS COUNT,
            LISTAGG (a.lastname, ' , ') WITHIN GROUP (ORDER BY a.id)
               AS lastname_stragg
       FROM person a, location b
      WHERE a.location_id = b.location_id
   GROUP BY ROLLUP (b.location_name)
     HAVING MAX (b.location_id) > 0
   ]';
END;
/  
------------------------------------------SQL QUERY------------------------------------------------------------
WITH SQL_QUERY AS
(SELECT UPPER(SUBSTR(sql_str, 0, REGEXP_INSTR(sql_str, ' FROM', 1,1,0, 'i'))) AS SELECT_STR,
              SUBSTR(sql_str, REGEXP_INSTR(sql_str, ' FROM', 1,1,0, 'i')) AS FROM_STR
 FROM
 (SELECT DBMS_XMLGEN.CONVERT( XMLQUERY('fn:substring(fn:normalize-space(str), 8) '
                              PASSING XMLELEMENT("str",  SQL_STR)
                              RETURNING CONTENT).GETSTRINGVAL(), 1) AS sql_str
  FROM (SELECT :sql_input as sql_str FROM dual )
 )     
),
COL_NAMES AS 
(SELECT SUBSTR(COL_NAME, 0, INSTR(COL_NAME, ' AS ')) AS VAL,  
        SUBSTR(COL_NAME, INSTR(COL_NAME, ' AS ') + 4) AS ALIAS, ROWNUM rn
 FROM SQL_QUERY, XMLTABLE('ora:tokenize(.,"#")'
                 PASSING  REGEXP_REPLACE (SELECT_STR, '([[:space:]]AS[[:space:]][^,]+)[[:space:]]?[,]?' , '\1#')
                 COLUMNS COL_NAME VARCHAR2 (100) PATH '.') 
),
XML_QUERY AS 
(SELECT ' SELECT 1, XMLELEMENT(ROW_DATA, '||LISTAGG(REPLACE(VAL, VAL, 'XMLELEMENT('||ALIAS||' , '||VAL||')') , ' , ') 
                                            WITHIN GROUP (ORDER BY rn)||')' AS SELECT_STR,
          MAX((SELECT FROM_STR FROM SQL_QUERY)) AS FROM_AND_CONDITIONS_STR
  FROM  COL_NAMES               
),
XML_DATA AS 
(SELECT DBMS_XMLGEN.GETXMLTYPE(DBMS_XMLGEN.NEWCONTEXTFROMHIERARCHY(
                               SELECT_STR||' '||FROM_AND_CONDITIONS_STR)) AS XML_DOC
 FROM XML_QUERY
)
SELECT XMLSERIALIZE(DOCUMENT XMLTRANSFORM(
(SELECT XMLELEMENT (ROWSET, XML_DOC) FROM XML_DATA),
 XMLTYPE (
'<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
     <html><body><xsl:apply-templates/> </body></html>
</xsl:template>
<xsl:template match="ROWSET">
     <table border = "1" cellspacing = "0"> <tr> <xsl:apply-templates select ="ROW_DATA[1]/*" mode ="Col_Name"/> </tr>
     <xsl:apply-templates/> </table>
</xsl:template>
<xsl:template match="ROW_DATA">
     <tr><xsl:apply-templates/> </tr>
</xsl:template>
<xsl:template match="ROW_DATA/*">
     <td><xsl:apply-templates/> </td>
</xsl:template>
<xsl:template match="ROW_DATA/*"  mode = "Col_Name">
      <th><xsl:value-of select ="name(.)"/> </th>
</xsl:template>
      </xsl:stylesheet>'))) AS HTML_TABL_DATA
FROM DUAL;              
  

HTML_TABL_DATA
——————————————————————————–

LOCATION COUNT LASTNAME_STRAGG
BOSTON 2 Zhou , Lee
NEW YORK 2 Adam , Washington
4 Zhou , Lee , Adam , Washington

Leave a Reply

You must be logged in to post a comment.

RSS feed for comments on this question