Trick #4 - Pull XML with DBMS_XMLGEN

September 23rd, 2006 by The Oracle Alchemist

Wouldn’t it be fantastic if you could simply pull data from the database in XML? Many shops use XML for data transfer, web services, reports, and more. Oracle’s XML Publisher product can retrieve XML from an HTTP feed and use it to generate rich reports with graphs, images, and other content, and then mail, fax, print, or FTP them.

All we need is the actual XML!

For this, we have the DBMS_XMLGEN package. This package generates XML on the fly using any query you want; in addition, it’s extremely easy to use from either the SQL prompt or in code, as it’s just a simple query.

Generating XML From the Database

Take this standard query:


SQL> select employee_id, first_name, last_name, phone_number
  2  from employees where rownum < 6

EMPLOYEE_ID FIRST_NAME           LAST_NAME                 PHONE_NUMBER
----------- -------------------- ------------------------- --------------------
        100 Steven               King                      515.123.4567
        101 Neena                Kochhar                   515.123.4568
        102 Lex                  De Haan                   515.123.4569
        103 Alexander            Hunold                    590.423.4567
        104 Bruce                Ernst                     590.423.4568

We get our standard output with no frills. Behold as we transform this into XML!


set pages 0
set linesize 150
set long 9999999
set head off
SQL> select dbms_xmlgen.getxml('select employee_id, first_name,
  2  last_name, phone_number from employees where rownum < 6') xml
  3  from dual

<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <EMPLOYEE_ID>100</EMPLOYEE_ID>
  <FIRST_NAME>Steven</FIRST_NAME>
  <LAST_NAME>King</LAST_NAME>
  <PHONE_NUMBER>515.123.4567</PHONE_NUMBER>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>101</EMPLOYEE_ID>
  <FIRST_NAME>Neena</FIRST_NAME>
  <LAST_NAME>Kochhar</LAST_NAME>
  <PHONE_NUMBER>515.123.4568</PHONE_NUMBER>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>102</EMPLOYEE_ID>
  <FIRST_NAME>Lex</FIRST_NAME>
  <LAST_NAME>De Haan</LAST_NAME>
  <PHONE_NUMBER>515.123.4569</PHONE_NUMBER>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>103</EMPLOYEE_ID>
  <FIRST_NAME>Alexander</FIRST_NAME>
  <LAST_NAME>Hunold</LAST_NAME>
  <PHONE_NUMBER>590.423.4567</PHONE_NUMBER>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>104</EMPLOYEE_ID>
  <FIRST_NAME>Bruce</FIRST_NAME>
  <LAST_NAME>Ernst</LAST_NAME>
  <PHONE_NUMBER>590.423.4568</PHONE_NUMBER>
 </ROW>
</ROWSET>

Beautiful! Fully compliant XML that can be easily integrated into any application, with ROWSET and ROW tags in place to identify nodes, and tags for each column you pulled out of the database.

All we had to do was wrap the query in the DBMS_XMLGEN.GETXML function call, selected from DUAL. The query remained exactly the same.

A More Advanced Use

Let’s take it one step further. Most XML has subnodes for each main node. For instance, what if we wanted to pull XML for every department, and a subnode for every employee under it? We can use the CURSOR function!


SQL> select department_id, department_name,
  2  cursor(select first_name, last_name
  3  from employees e
  4  where e.department_id = d.department_id) emp_row
  5  from departments d
  6* where rownum < 4

DEPARTMENT_ID DEPARTMENT_NAME                EMP_ROW
------------- ------------------------------ --------------------
           10 Administration                 CURSOR STATEMENT : 3

CURSOR STATEMENT : 3

FIRST_NAME           LAST_NAME
-------------------- -------------------------
Jennifer             Whalen

           20 Marketing                      CURSOR STATEMENT : 3

CURSOR STATEMENT : 3

FIRST_NAME           LAST_NAME
-------------------- -------------------------
Michael              Hartstein
Pat                  Fay

           30 Purchasing                     CURSOR STATEMENT : 3

CURSOR STATEMENT : 3

FIRST_NAME           LAST_NAME
-------------------- -------------------------
Den                  Raphaely
Alexander            Khoo
Shelli               Baida
Sigal                Tobias
Guy                  Himuro
Karen                Colmenares

6 rows selected.

The results don’t look too impressive at the SQL prompt. However, watch as we surround it with a call to DBMS_XMLGEN.GETXML:


SQL> select dbms_xmlgen.getxml('
  2  select department_id, department_name,
  3  cursor(select first_name, last_name
  4  from employees e
  5  where e.department_id = d.department_id) emp_row
  6  from departments d
  7  where rownum < 4
  8* ') from dual

<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <DEPARTMENT_ID>10</DEPARTMENT_ID>
  <DEPARTMENT_NAME>Administration</DEPARTMENT_NAME>
  <EMP_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Jennifer</FIRST_NAME>
    <LAST_NAME>Whalen</LAST_NAME>
   </EMP_ROW_ROW>
  </EMP_ROW>
 </ROW>
 <ROW>
  <DEPARTMENT_ID>20</DEPARTMENT_ID>
  <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME>
  <EMP_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Michael</FIRST_NAME>
    <LAST_NAME>Hartstein</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Pat</FIRST_NAME>
    <LAST_NAME>Fay</LAST_NAME>
   </EMP_ROW_ROW>
  </EMP_ROW>
 </ROW>
 <ROW>
  <DEPARTMENT_ID>30</DEPARTMENT_ID>
  <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME>
  <EMP_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Den</FIRST_NAME>
    <LAST_NAME>Raphaely</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Alexander</FIRST_NAME>
    <LAST_NAME>Khoo</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Shelli</FIRST_NAME>
    <LAST_NAME>Baida</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Sigal</FIRST_NAME>
    <LAST_NAME>Tobias</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Guy</FIRST_NAME>
    <LAST_NAME>Himuro</LAST_NAME>
   </EMP_ROW_ROW>
   <EMP_ROW_ROW>
    <FIRST_NAME>Karen</FIRST_NAME>
    <LAST_NAME>Colmenares</LAST_NAME>
   </EMP_ROW_ROW>
  </EMP_ROW>
 </ROW>
</ROWSET>

Note that I didn’t change the query in any way. But check out the results! We have each DEPARTMENT as a ROW tag, and the cursor we created gives us an EMP_ROW node containing recurring EMP_ROW_ROW nodes.

Conclusion

This can be extremely useful for quick retrieval of web records. Simply make a page that accepts input, such as DEPARTMENT_ID, and passes it into the query above. Display the response directly to the screen, and you have an easy XML display program. The best part comes with reports. XML Publisher is made to accept XML that looks just like this and form extremely detailed reports using templates made in Microsoft Word. With queries such as these and XML Publisher you can have a full reporting suite that easily pulls data, forms it into a PDF, DOC, XLS, or HTML report, and distributes it anywhere you would like it to go.

26 Responses to “Trick #4 - Pull XML with DBMS_XMLGEN”

  1. Ralf Kanis Says:

    Pretty cool! But there is one problem if I try this with a simple query. I just get something like this:

    XML
    ——————————————————————————–

    0
    zzz_frei_Rechts

    1 row selected.

    Seems that it stops in the middle of nowhere with the output. Any suggestions?

    Best Regards
    Ralf Kanis

  2. Ashley Jose Says:

    Well written. Excellent article.I am new to XML publisher. I would like to know how I can integrate the generated XML with the layout template that I have created [RTF] using XML publisher.

    Thanks,
    Ash

  3. steve Says:

    Ralf, did you try running these lines before the query?

    set pages 0
    set linesize 150
    set long 9999999
    set head off

    The result looks odd (no tags), but getting cut off in the middle of nowhere usually happens when you don’t use “set long.”

    If that doesn’t fix it, can you post your query?

  4. steve Says:

    Ashley,

    Thank you very much for your compliment. Are you trying to integrate using the Enterprise publisher, or the Word plugin?

    If it’s the word plugin, you can simply use a query in the wizard, it will automatically wrap DBMS_XMLGEN around it to make it work. Or you can generate an XML file using the methods I have outlined in my article then open the XML in Word to make it work with your template.

    If you are talking about the Enterprise version, you can always spool the XML to a file on a webserver. In the XML Publisher Enterprise setup, just set up the source as an HTTP Feed pointing to the generated file.

    I’d recommend this article to help you get started: http://www.oracle.com/technology/pub/articles/rittman-xmlpub.html

  5. VIVEK KHANNA Says:

    Great Article!!!. I am new toXML publisher. I want to know is that how will I ahndle empty tags. I do not want empty tags to appear in my XML….How can I ignore empty tags.
    in the below eg i don’t want to publish emp_row tag.

    10
    FINANCE

    Thanks for the help..

  6. Sharanappa Says:

    Hi,

    The dbms_xmlgen.getxml package is very good for Populate xml tags.
    Is there any other advantage from this package?

    Thanks,
    Sharan.

  7. The Oracle Alchemist Says:

    Sharanappa,

    Well, populating tags is really all it does, but there is more you can do with the package. You can have the package check for special characters and escape them (setConvertSpecialChars), or check for invalid characters (setCheckInvalidChars). There are many ways to format your XML to your exact liking.

  8. The Oracle Alchemist Says:

    Vivek,

    By default, DBMS_XMLGEN.getXML will leave out null tags. Take a look here.

    I’m not exactly seeing what you’re hoping to accomplish in the example you pasted. Perhaps a little more detail? You can email me if you like.

  9. ashleyjose Says:

    Hi,

    Before starting thanks to the answer you had given me previoulsy.

    This time I have 2 questions over here.

    1. For creating reports using XML publisher I am now creating the xml file [using dbms_xmlgen] via a concurrent program and then merging it with the layout template. What do u feel ,should I really use a Data Template for generating the XML output file or will xmlgen be enough?

    2.See the below code.

    PROCEDURE MAIN ( errbuf OUT VARCHAR2
    ,retcode OUT NUMBER
    ) IS

    v_query_ref g_query_ref;

    v_handle dbms_xmlgen.ctxhandle;
    v_result clob;

    BEGIN

    OPEN v_query_ref FOR
    SELECT DECODE(ppt.system_person_type,’EMP’,'N’,'T’) action_code
    ,papf.attribute1 leg_indentifier
    , TO_CHAR(papf.attribute3 ,’DD-MON-YYYY’) retirement_date
    ;

    v_handle := dbms_xmlgen.newContext(v_query_ref);

    DBMS_XMLGEN.SETROWTAG (v_handle , ‘EMPLOYEE’);

    v_result := dbms_xmlgen.getXML(v_handle);

    fnd_file.put_line(fnd_file.output,v_result);

    EXCEPTION
    when others then
    errbuf := sqlerrm;
    retcode := sqlcode;
    fnd_file.put_line(fnd_file.log,’sqlerrm ‘||sqlerrm);

    END MAIN;

    The sql returns more than 3200 records. My problem is fnd_file(fnd_file.output,v_result) is giving me ORA -06502

    When I changed the cursor so that it would just return me 5 records fnd_file worked fine.

    How do I handle this?

    Thanks in Advance,
    Ashley

  10. The Oracle Alchemist Says:

    Ashley,

    1. A data template is good because it will work with the concurrent manager with minimal fuss, but it sounds like you got it. I prefer generating the XML myself, because then I have greater flexibility (as you clearly show in your example code).

    2. The problem appears to be flushing. FND_FILE, like UTL_FILE, can only write 32k at a time before it must be flushed. You may want to try taking some of the recommendations found in this forum thread I found.

  11. wcaples Says:

    Steve,
    Thanks for helping to bridge the gap in my understanding between the XML Publisher docs and actual reports! Maybe you can help me get over one last hurdle: I’m trying to work with the nested XML that you generate with your cursor trick. I created an .rtf template in the Word plugin using the SQL without the DBMS_XMLGEN, and that previews fine. Then, in XML Publisher Enterprise, I uploaded that template as a layout, and created a data model using the same query. However, I cannot view the report, I get an error saying that the report cannot be rendered because of an error. I have tried making the data model with the DBMS_XMLGEN wrapper and without it, and get the same error both ways. Am I still unclear on the concept of how these two pieces fit together?

  12. The Oracle Alchemist Says:

    You don’t need the XMLGen wrapper when doing it as a data model.

    The error you’re getting is a generic one, and can be caused by ANY problem with your report. Try editing the report and making sure all your information is correct in every single section. Make sure the data source name is right in all places, and that it’s being run as a user with proper permissions to access the data.

  13. Jay Says:

    Great article. Found very useful.
    Thanks.

  14. darcy Says:

    Hey Steve- long time no chat - so I am using dbms_xmlgen.getXML to try and create an xml file containing 326,000 records - and the file never gets past 11000 recs. I don’t think it is a spool issue - as I have spooled files over 40MB before - is there some internal milit with this function ? or am I missing something in my set commands ?

    set serveroutput on size 1000000
    set head off;
    set long 9999999;
    set pages 0;
    set linesize 150;
    set buffer 6000000;
    set feedback off;
    spool vast_automart.xml;

    select dbms_xmlgen.getXML(’select… from temp table’) from dual;…

  15. The Oracle Alchemist Says:

    Darcy,

    Hey, good to see you!

    You said your query was “select dbms_xmlgen.getXML(’select… from temp table’) from dual”, but that “…” is going to be very important.

    You set your “long” to 9,999,999. With 326,000 records, you would only be allowed a maximum of 30.67 characters per row. All it will take is a single column with a 13 character column name to use that!

    The maximum for “set long” is 2 billion…that will leave enough for 6,134.97 characters per row, which is definitely more realistic. If you’re getting 11,000 rows with a “set long” value of 9,999,999, then you’re averaging ~910 characters per row of XML, so that requires a set long of at least 296,660,000.

  16. Staind_art Says:

    Hi,

    I have been working with XMLP for a little while now and I am facing an hard issue here, I have to generate a report containing 3 queries and i am not able to find any documentation explaining in details or even roughly how to proceed.
    I am hoping that in the past you faced a similar issue and will be able to guide me into the right direction.

    Regards,

    Staind

  17. Lori Says:

    Great Article! I am just becoming familar with all of this so the examples are really helpful.

    I have a question regarding the tags:

    1. is there an easy way to change the tag names. when i ran a simple “select count(*)…” in this dbms_xmlgen i received the following:

    8

    is there a way i can change the tag field?

    thanks,

    Lori

  18. Lori Says:

    oops….it didn’t show on the website correctly.

    this is what i am getting:

    COUNT_x0028__x002A__x0029_ in the tag fields around the 8

    thanks

  19. Easy Stored Procedure Output · Steve Karam · The Oracle Alchemist Says:

    [...] #3 is more in-line with your original needs. Personally I’m a fan of #1 and #2 because of the capabilities of returning a refcursor as a function, like passing it into DBMS_XMLGEN.GETXML. [...]

  20. Jose Francisco Says:

    Hi,
    I would like to return two query’s which have not relation themselves in the same xml. I mean, the result could be:

    How coud I perform the query and the PLSQL code?

    Thanks in advance

  21. Sreeni Says:

    Hi ,

    Data in one of the columns of my query has relatively more length(300 characters ).
    When I used DBMS_XMLGEN to generate xml that element is getting printed in 2 rows.
    Example:

    dataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    aaaaaaaaaaaaaaaaaaaaaaaa

    When my xml is fed to XML Publisher to generate report it is failed with unable to parse XML error.

    Any idea how to print the element in single line.I tried with DBMS_QUERYXML.GETXML.
    It worked for some time and failed again.

    Thanks in advance.

  22. Derrin Says:

    Hi

    Similar to Sreeni’s question

    using this method great - but the elements (forest items) won’t write to individual lines in the spool. lines with multiple attributes are then wrapped, and effectively chopping the elements at the line wrap level - tried altering linesize - set it to 150 and it wraps at 100 !

    Here is my sho all
    SQL> sho all
    appinfo is OFF and set to “SQL*Plus”
    arraysize 15
    autocommit OFF
    autoprint OFF
    autorecovery OFF
    autotrace OFF
    blockterminator “.” (hex 2e)
    btitle OFF and is the first few characters of the next SELECT statement
    cmdsep OFF
    colsep ” ”
    compatibility version NATIVE
    concat “.” (hex 2e)
    copycommit 0
    COPYTYPECHECK is ON
    define “&” (hex 26)
    describe DEPTH 1 LINENUM OFF INDENT ON
    echo OFF
    editfile “afiedt.buf”
    embedded OFF
    escape OFF
    FEEDBACK ON for 6 or more rows
    flagger OFF
    flush ON
    heading OFF
    headsep “|” (hex 7c)
    instance “local”
    linesize 200
    lno 1
    loboffset 1
    logsource “”
    long 1000000000
    longchunksize 2000000000
    markup HTML OFF HEAD ” body {font:10pt Arial,Helvetica,sans-serif; color:blac
    newpage 1
    null “”
    numformat “”
    numwidth 10
    pagesize 0
    PAUSE is OFF
    pno 0
    recsep WRAP
    recsepchar ” ” (hex 20)
    release 1002000100
    repfooter OFF and is NULL
    repheader OFF and is NULL
    serveroutput ON SIZE UNLIMITED FORMAT TRUNCATED
    shiftinout INVISIBLE
    showmode OFF
    spool OFF
    sqlblanklines OFF
    sqlcase MIXED
    sqlcode 0
    sqlcontinue “> ”
    sqlnumber ON
    sqlpluscompatibility 10.2.0
    sqlprefix “#” (hex 23)
    sqlprompt “SQL> ”
    sqlterminator “;” (hex 3b)
    suffix “sql”
    tab ON
    termout ON
    timing OFF
    trimout OFF
    trimspool OFF
    ttitle OFF and is the first few characters of the next SELECT statement
    underline “-” (hex 2d)
    USER is “DERRIN”
    verify ON
    wrap : lines will be wrapped

    Thanks

  23. The Oracle Alchemist Says:

    Are you spooling the data? Here are the ones I’ve had the most luck with:

    set long 999999999
    set linesize 32767
    set longchunksize 999999999
    set feedback off
    set verify off
    set pagesize 0
    set trimspool on long 999999999

  24. Vish Says:

    Hi there,

    I have been toying with XML file generation thru dbms_xmlgen.NewContext but have been having problems suppressing multiple tags. I have something like the following-
    dbms_xmlgen.newcontext(’SELECT /*+ FIRST_ROWS PARALLEL(a11,16) PARALLEL(a12, 16) */ XMLELEMENT(”provider”, XMLForest(DECODE(a13.HOSPITAL_ID,”C”,110078,”E”,110010) as “provider-id”),XMLELEMENT( “patient”, XMLForest ( a111.PATIENT_FIRST_NM “first-name”, a111.PATIENT_LAST_NM “last-name” ),XMLELEMENT(”encounter”, XMLATTRIBUTES ( ”HOP-SURGERY” AS “measure-set”),
    XMLForest ( a113.DAY_ADMIT_DESC as “encounter-date”,a110.PLAN_INSURANCE_CARD_NBR “patient-HIC-Number”, a111.PATIENT_ID as “patient-id”, a12.MEDICAL_RECORD_NBR as “medical-record-nbr”)
    ))) as “provider” FROM ….’);
    When I write the XML Doc to a table, I get multiple “provider” tags in the document. I have defined setrowsettag to be “provider” but in addition to this I am getting the two embedded labels as well. How do I suppress them from showing up at all? All I want is just one tag defined in the setrowsettag.

    Please help!!

    VR

  25. Daniel Says:

    Your suggestion of setting in sql*plus the following parameters

    set pages 0
    set linesize 150
    set long 9999999
    set head off

    Worked perfectly.

    Thank you for a clear and precise explanation of how to get XML data out of Oracle - Now why can Oracle not do anything this simple and efficient to show how their technologies work?

    Cheers,
    Daniel

  26. ravikumar Says:

    Good Post.. I have been trying in vain to remove the ‘_ROW’ tag that is displayed in the sub nodes. I know it differentiates the different rows. But my requirement is such that I need to remove the ‘_ROW’ tag.

    Can someone help me find a way ?? It’s urgent for me.

Leave a Reply



Related Posts


Easy Stored Procedure Output
I answered a question on the DBA Forum today and I thought it was a common enough question to warrant a blog posting. You can find the entire thread here.  ...
Trick #1 - Copying Users the Right Way
Yesterday I announced that I would be doing a five day series entitle "The 5 Oracle Tricks You Never Knew." Today's trick is pretty simple, and it allows you ...
Oracle Tricks - Many Apologies
It seems I've run out of time in the day...I had hoped to provide an article today on Trick #3, yet work has piled up and I was not able ...
Trick #2 - Rewrite a Query at Runtime
There are many Oracle tools that can wreak havoc on an unsuspecting DBA or developer, but all in all they are for the common good. Though Oracle never puts ...
Mac vs. PC Wars - PCs Strike Back
I'm a Mac user, and I have to say I love my Mac. Not because I think it's horribly superior to a PC or any of that; I just ...