XMLTABLE: Convert XML Data to Relational Form

August 16, 2017

in PL/SQL, Views, XML DB

XMLTABLE to Convert XML To Relational Data

Here’s a prototype for using the SQL/XML function XMLTABLE to map XML data into relational rows and columns.

This solution uses the standard EMP table — the same can be extended to work with any XMLTYPE-relational mapping.

Problem Statement: Receive XML Payload, Parse and Store in Relational Table

An application receives employee data in XML format. The XML payload contains details of a list of employees associated with a department and hire date. For example, the payload for department 10 and hire date 2016-APR-02, with two employees, looks like this:

<data>
  <deptNo>10</deptNo> 
  <hireDate>2016-APR-02</hireDate>            
  <!--List of employees with common deptNo and hireDate-->
  <empList>   
    <emp>            
        <empNo>98</empNo>            
        <eName>KINGSLEY SHACKLEBOLT</eName>
        <job>AGENT</job>            
        <salary>50000</salary>            
    </emp>            
    <emp>            
        <empNo>99</empNo>            
        <eName>FILIUS FLITWICK</eName> 
        <job>PROFESSOR</job>            
        <salary>44000</salary>            
    </emp>            
  </empList> 
</data>

An external system inserts such XML payloads into an XMLTYPE column in staging table EMP_PAYLOAD, with status PENDING.

Table EMP_PAYLOAD has this structure:

create table emp_payload
(
  messageid    NUMBER
, status       VARCHAR2(10)
, empxml       XMLTYPE
);

A scheduled program needs to pick records with status PENDING from EMP_PAYLOAD, map them to EMP table, and:

  • INSERT into EMP table if the records are new
  • UPDATE the records in EMP table if the records exist already

Once done, the PENDING status in EMP_PAYLOAD is to be changed to PROCESSED.

Solution Summary

This requirement can be met by:

  • using XMLTABLE to parse the XML data into relational rows and columns
  • wrapping a MERGE statement around the relational representation of the XML data to update/insert into the target table
  • post-MERGE, issuing a simple UPDATE on the source table to change status from PENDING to PROCESSED

PL/SQL script solution with XMLTABLE

A PL/SQL script to achieve the required result. A description of XMLTABLE and run result follows.

begin
  merge into emp a
  using 
    (select  empno   
   , ename   
   , job     
   , hiredate
   , sal     
   , deptno
  from emp_payload m
     , xmltable('//data//emp' passing m.empxml
       columns 
             empno      NUMBER(4)    PATH 'empNo'
           , ename      VARCHAR2(30) PATH 'eName'
           , job        VARCHAR2(9)  PATH 'job' 
           , hiredate   DATE         PATH './../../hireDate'
           , sal        NUMBER(7,2)  PATH 'salary'
           , deptno     NUMBER(2)    PATH './../../deptNo'
                 ) t
       where status = 'PENDING') b
  on  (a.empno = b.empno)
  when matched then
  update set 
        a.ename            = b.ename
      , a.job              = b.job
      , a.sal              = b.sal
      , a.deptno           = b.deptno
  when not matched then
  insert 
     (empno   
   , ename   
   , job     
   , hiredate
   , sal     
   , deptno)
  values
    (b.empno   
   , b.ename   
   , b.job     
   , b.hiredate
   , b.sal     
   , b.deptno);
 
   update emp_payload 
   set status = 'PROCESSED' where status = 'PENDING';

end;
/

[Implemented in Oracle 12.1]

Understanding the SQL/XML function XMLTABLE

The SQL/XML function XMLTABLE is used in the FROM clause of a SQL, in conjunction with a driving table that holds XML data, to convert the XML data to relational form. Interestingly, XMLTABLE joins implicitly with its driving table; no join condition needs to be spelled out for it.

  • The PASSING clause specifies the XMLTYPE data to be processed by the XMLTABLE function.
  • The COLUMNS clause projects the XML data into relational form. Each entry in the COLUMNS clause defines a virtual table column, assigning it a column name and data type.
  • The PATH expression uses XQuery to maps the columns to XML elements in the payload.

PL/SQL script solution with XMLTABLE: Run results

Data in EMP table before merging the XML payload:

SQL> SELECT empno
  2   , ename
  3   , job
  4   , hiredate
  5   , sal
  6   , deptno
  7  FROM emp;

EMPNO ENAME                 JOB       HIREDATE     SAL     DEPTNO
----- --------------------- --------- --------- ------ ----------
 7788 SCOTT                 ANALYST   19-APR-87   3000         20
 7876 ADAMS                 CLERK     23-MAY-87   1100         20
   98 KINGSLEY SHACKLEBOLT  AUROR     02-APR-16  45000         10

XML payload with PENDING status:

SQL> select * from emp_payload;

MESSAGEID STATUS  EMPXML
--------- ------- ------------------------------------------
        1 PENDING <data>
                    <deptNo>20</deptNo>
                    <hireDate>2017-AUG-13</hireDate>
                    <!--List of employees with common deptNo
                  and hireDate-->
                    <empList>
                      <emp>
                        <empNo>97</empNo>
                        <eName>BATHILDA BAGSHOT</eName>
                        <job>HISTORIAN</job>
                        <salary>55000</salary>
                      </emp>
                    </empList>
                  </data>

        2 PENDING <data>
                    <deptNo>10</deptNo>
                    <hireDate>2016-APR-02</hireDate>
                    <!--List of employees with common deptNo
                  and hireDate-->
                    <empList>
                      <emp>
                        <empNo>98</empNo>
                        <eName>KINGSLEY SHACKLEBOLT</eName>
                        <job>AGENT</job>
                        <salary>50000</salary>
                      </emp>
                      <emp>
                        <empNo>99</empNo>
                        <eName>FILIUS FLITWICK</eName>
                        <job>PROFESSOR</job>
                        <salary>44000</salary>
                      </emp>
                    </empList>
                  </data>

We see two rows in the EMP_PAYLOAD table, containing three employee records to be processed overall.

After merging the XML payload:
– Two new rows (empnos 97 and 99) should be inserted
– One row (empno 98) should be updated

Script run:

SQL> begin
  2    merge into emp a
  3    using
  4      (select  empno
  5     , ename
  6     , job
  7     , hiredate
  8     , sal
  9     , deptno
 10    from emp_payload m
 11       , xmltable('//data//emp' passing m.empxml
 12         columns
 13               empno      NUMBER(4)    PATH 'empNo'
 14             , ename      VARCHAR2(30) PATH 'eName'
 15             , job        VARCHAR2(9)  PATH 'job'
 16             , hiredate   DATE         PATH './../../hireDate'
 17             , sal        NUMBER(7,2)  PATH 'salary'
 18             , deptno     NUMBER(2)    PATH './../../deptNo'
 19                   ) t
 20         where status = 'PENDING') b
 21    on  (a.empno = b.empno)
 22    when matched then
 23    update set
 24          a.ename            = b.ename
 25        , a.job              = b.job
 26        , a.sal              = b.sal
 27        , a.deptno           = b.deptno
 28    when not matched then
 29    insert
 30       (empno
 31     , ename
 32     , job
 33     , hiredate
 34     , sal
 35     , deptno)
 36    values
 37      (b.empno
 38     , b.ename
 39     , b.job
 40     , b.hiredate
 41     , b.sal
 42     , b.deptno);
 43
 44     update emp_payload
 45     set status = 'PROCESSED' where status = 'PENDING';
 46
 47  end;
 48  /

PL/SQL procedure successfully completed.

Data in EMP table after merging the XML payload:

SQL> SELECT empno
  2   , ename
  3   , job
  4   , hiredate
  5   , sal
  6   , deptno
  7  FROM emp;

EMPNO ENAME                 JOB       HIREDATE     SAL     DEPTNO
----- --------------------- --------- --------- ------ ----------
 7788 SCOTT                 ANALYST   19-APR-87   3000         20
 7876 ADAMS                 CLERK     23-MAY-87   1100         20
   98 KINGSLEY SHACKLEBOLT  AGENT     02-APR-16  50000         10
   97 BATHILDA BAGSHOT      HISTORIAN 13-AUG-17  55000         20
   99 FILIUS FLITWICK       PROFESSOR 02-APR-16  44000         10

After running the PL/SQL script using XMLTABLE, we do see the result in EMP table as expected.
– Two new rows (empnos 97 and 99) have been inserted, taking the record count up from 3 to 5
– For empno 98, the job and salary have been updated

Summary

This article describes the SQL/XML function XMLTABLE, with a working prototype for interpreting XML data and converting it to relational form.

Want to do the reverse – that is, converting relational data to XML form? Read Generating XML from Relational Data using SQL/XML to know how.

Leave a Comment

Previous post:

Next post: