
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.








