It is a fact that we, as software developer, will encounter more and more data in XML format. Either we receive xml data or actually need to produce one for information sharing with other system (web service, legacy system, .net vs Java, etc...).
XMLDB of Oracle database (version 9i Release 2 and above) provides us with a convenience data type (XMLType) to store xml data, as well as the full set of functions for manipulating and accessing it.
Action speak loader than words. So let's see how easy XMLDB actually is...
Using the default SCOTT schema, let's assume that we need to create xml data showing all employee details. We shall create 2 tables:
1. dept_xml: a huge xml doc of all department and the employee within it.
2. emp_xml: all employee information in a xml snippet
And here we go:
1. Create the tables with XMLType column for storing the xml data.
scott> create table dept_xml(
2 xml XMLType);
Table created.
scott> create table emp_xml(
2 xml XMLType);
Table created.
2. Transform the relational data from DEPT and EMP table into xml document using SQL functions and PL/SQL
-- insert a big xml doc to dept_xml table
scott> insert into dept_xml values ((SELECT XMLRoot(XMLElement("ORG_CHART", XMLAgg(XMLElement("DEPARTMENT",
2 XMLAttributes(d.deptno AS "DEPTNO",
3 d.dname AS "NAME",
4 d.loc AS "LOCATION"),
5 XMLForest(
6 (SELECT XMLAgg(
7 XMLElement(
8 "EMPLOYEE",
9 xmlforest(e.empno,
10 e.ename,
11 e.job,
12 e.mgr,
13 e.hiredate,
14 e.sal,
15 e.comm,
16 s.grade
17 )
18 )
19 )
20 FROM emp e, salgrade s
21 WHERE e.deptno = d.deptno
22 AND e.sal BETWEEN s.losal and s.hisal
23 ) "EMPLOYEES"
24 )
25 )))) "XML"
26 FROM dept d
27 ));
1 row created.
-- insert each employee data as a separate xml doc
scott> DECLARE
2 empNo NUMBER;
3 mysql VARCHAR2(512);
4 CURSOR empNoCursor IS
5 select empno
6 from emp;
7 BEGIN
8 OPEN empNoCursor;
9 LOOP
10 FETCH empNoCursor INTO empNo;
11 EXIT WHEN empNoCursor %NOTFOUND;
12
13 --DBMS_OUTPUT.put_line('empNo: ' || empNo);
14
15 mysql := 'insert into emp_xml values ((SELECT XMLRoot( ' ||
16 ' XMLElement( ' ||
17 ' "EMPLOYEE", ' ||
18 ' xmlforest(e.empno, e.ename, e.job, e.mgr, e.hiredate, e.sal, e.comm, e.deptno, s.grade))) ' ||
19 ' )) ' ||
20 ' FROM emp e, salgrade s ' ||
21 ' WHERE e.empno = :1 ' ||
22 ' AND e.sal BETWEEN s.losal and s.hisal ' ||
23 ' )) ';
24
25 EXECUTE IMMEDIATE mysql using empNo;
26
27 END LOOP;
28 CLOSE empNoCursor;
29 COMMIT;
30 END;
31 /
PL/SQL procedure successfully completed.
Here we use the following XML SQL Utility:
XMLRoot: create a XML document of XMLType
XMLAgg: aggregate a bunch of XML elements and group it as one forest
XMLElement: create a XML element
XMLAttributes: create a XML attribute
XMLForest: take XMLAgg result and group them as a XML node
Please note that for dept_xml entry, we also created our custom tag named: ORG_CHART, DEPARTMENT, EMPLOYEES, and EMPLOYEE
Check
Oracle documentation for syntax and more explanation.
3. Final result:
XML
-------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<ORG_CHART>
<DEPARTMENT DEPTNO="10" NAME="ACCOUNTING" LOCATION="NEW YORK">
<EMPLOYEES>
<EMPLOYEE>
<EMPNO>7839</EMPNO>
<ENAME>KING</ENAME>
<JOB>PRESIDENT</JOB>
<HIREDATE>1981-11-17</HIREDATE>
<SAL>5000</SAL>
<GRADE>5</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7782</EMPNO>
<ENAME>CLARK</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-06-09</HIREDATE>
<SAL>2450</SAL>
<GRADE>4</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7934</EMPNO>
<ENAME>MILLER</ENAME>
<JOB>CLERK</JOB>
<MGR>7782</MGR>
<HIREDATE>1982-01-23</HIREDATE>
<SAL>1300</SAL>
<GRADE>2</GRADE>
</EMPLOYEE>
</EMPLOYEES>
</DEPARTMENT>
<DEPARTMENT DEPTNO="20" NAME="RESEARCH" LOCATION="DALLAS">
<EMPLOYEES>
<EMPLOYEE>
<EMPNO>7788</EMPNO>
<ENAME>SCOTT</ENAME>
<JOB>ANALYST</JOB>
<MGR>7566</MGR>
<HIREDATE>1987-04-19</HIREDATE>
<SAL>3000</SAL>
<GRADE>4</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7902</EMPNO>
<ENAME>FORD</ENAME>
<JOB>ANALYST</JOB>
<MGR>7566</MGR>
<HIREDATE>1981-12-03</HIREDATE>
<SAL>3000</SAL>
<GRADE>4</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7566</EMPNO>
<ENAME>JONES</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-04-02</HIREDATE>
<SAL>2975</SAL>
<GRADE>4</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7876</EMPNO>
<ENAME>ADAMS</ENAME>
<JOB>CLERK</JOB>
<MGR>7788</MGR>
<HIREDATE>1987-05-23</HIREDATE>
<SAL>1100</SAL>
<GRADE>1</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7369</EMPNO>
<ENAME>SMITH</ENAME>
<JOB>CLERK</JOB>
<MGR>7902</MGR>
<HIREDATE>1980-12-17</HIREDATE>
<SAL>800</SAL>
<GRADE>1</GRADE>
</EMPLOYEE>
</EMPLOYEES>
</DEPARTMENT>
<DEPARTMENT DEPTNO="30" NAME="SALES" LOCATION="CHICAGO">
<EMPLOYEES>
<EMPLOYEE>
<EMPNO>7698</EMPNO>
<ENAME>BLAKE</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-05-01</HIREDATE>
<SAL>2850</SAL>
<GRADE>4</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7499</EMPNO>
<ENAME>ALLEN</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-02-20</HIREDATE>
<SAL>1600</SAL>
<COMM>300</COMM>
<GRADE>3</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7844</EMPNO>
<ENAME>TURNER</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-09-08</HIREDATE>
<SAL>1500</SAL>
<COMM>0</COMM>
<GRADE>3</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7521</EMPNO>
<ENAME>WARD</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-02-22</HIREDATE>
<SAL>1250</SAL>
<COMM>500</COMM>
<GRADE>2</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7654</EMPNO>
<ENAME>MARTIN</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-09-28</HIREDATE>
<SAL>1250</SAL>
<COMM>1400</COMM>
<GRADE>2</GRADE>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7900</EMPNO>
<ENAME>JAMES</ENAME>
<JOB>CLERK</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-12-03</HIREDATE>
<SAL>950</SAL>
<GRADE>1</GRADE>
</EMPLOYEE>
</EMPLOYEES>
</DEPARTMENT>
<DEPARTMENT DEPTNO="40" NAME="OPERATIONS" LOCATION="BOSTON"/>
</ORG_CHART>
scott> select * from emp_xml;
XML
-------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7369</EMPNO>
<ENAME>SMITH</ENAME>
<JOB>CLERK</JOB>
<MGR>7902</MGR>
<HIREDATE>1980-12-17</HIREDATE>
<SAL>800</SAL>
<DEPTNO>20</DEPTNO>
<GRADE>1</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7499</EMPNO>
<ENAME>ALLEN</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-02-20</HIREDATE>
<SAL>1600</SAL>
<COMM>300</COMM>
<DEPTNO>30</DEPTNO>
<GRADE>3</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7521</EMPNO>
<ENAME>WARD</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-02-22</HIREDATE>
<SAL>1250</SAL>
<COMM>500</COMM>
<DEPTNO>30</DEPTNO>
<GRADE>2</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7566</EMPNO>
<ENAME>JONES</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-04-02</HIREDATE>
<SAL>2975</SAL>
<DEPTNO>20</DEPTNO>
<GRADE>4</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7654</EMPNO>
<ENAME>MARTIN</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-09-28</HIREDATE>
<SAL>1250</SAL>
<COMM>1400</COMM>
<DEPTNO>30</DEPTNO>
<GRADE>2</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7698</EMPNO>
<ENAME>BLAKE</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-05-01</HIREDATE>
<SAL>2850</SAL>
<DEPTNO>30</DEPTNO>
<GRADE>4</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7782</EMPNO>
<ENAME>CLARK</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-06-09</HIREDATE>
<SAL>2450</SAL>
<DEPTNO>10</DEPTNO>
<GRADE>4</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7788</EMPNO>
<ENAME>SCOTT</ENAME>
<JOB>ANALYST</JOB>
<MGR>7566</MGR>
<HIREDATE>1987-04-19</HIREDATE>
<SAL>3000</SAL>
<DEPTNO>20</DEPTNO>
<GRADE>4</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7839</EMPNO>
<ENAME>KING</ENAME>
<JOB>PRESIDENT</JOB>
<HIREDATE>1981-11-17</HIREDATE>
<SAL>5000</SAL>
<DEPTNO>10</DEPTNO>
<GRADE>5</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7844</EMPNO>
<ENAME>TURNER</ENAME>
<JOB>SALESMAN</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-09-08</HIREDATE>
<SAL>1500</SAL>
<COMM>0</COMM>
<DEPTNO>30</DEPTNO>
<GRADE>3</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7876</EMPNO>
<ENAME>ADAMS</ENAME>
<JOB>CLERK</JOB>
<MGR>7788</MGR>
<HIREDATE>1987-05-23</HIREDATE>
<SAL>1100</SAL>
<DEPTNO>20</DEPTNO>
<GRADE>1</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7900</EMPNO>
<ENAME>JAMES</ENAME>
<JOB>CLERK</JOB>
<MGR>7698</MGR>
<HIREDATE>1981-12-03</HIREDATE>
<SAL>950</SAL>
<DEPTNO>30</DEPTNO>
<GRADE>1</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7902</EMPNO>
<ENAME>FORD</ENAME>
<JOB>ANALYST</JOB>
<MGR>7566</MGR>
<HIREDATE>1981-12-03</HIREDATE>
<SAL>3000</SAL>
<DEPTNO>20</DEPTNO>
<GRADE>4</GRADE>
</EMPLOYEE>
<?xml version="1.0" encoding="UTF-8"?>
<EMPLOYEE>
<EMPNO>7934</EMPNO>
<ENAME>MILLER</ENAME>
<JOB>CLERK</JOB>
<MGR>7782</MGR>
<HIREDATE>1982-01-23</HIREDATE>
<SAL>1300</SAL>
<DEPTNO>10</DEPTNO>
<GRADE>2</GRADE>
</EMPLOYEE>
14 rows selected.
As you can see, creating XML data from traditional relational table is a breeze with Oracle XML utilities. Instead of having to retrieve the table data, create a XML document in the middle tier, and storing back to the database, we achieve the goal in just a one SQL statement! Not to mention the performance gain and scalability of the system.
In next blog, we shall see how we can access these XML info with ease, too. Stay tune...