This week's blog article was written by Nick Lawrence. Nick works for IBM in Rochester, Minn., on DB2 for IBM i. His responsibilities include full-text search for DB2 and SQL/XML. Thanks, Nick!
In this most recent round of announcements, IBM has included support for the XMLTABLE table function in SQL. XMLTABLE is designed to convert an XML document into a relational result set (rows and columns) using popular XPath expressions. This function has been referred to as the Swiss army knife for working with XML because it can help solve a wide variety of XML related problems.
A good tutorial of what XMLTABLE is and how it works can be found in the SQL XML Reference (a new book in the Info Center). The examples show how XMLTABLE can be used to perform a query over XML data in a DB2 column.
What may not be obvious from the documentation is that, the XML support included in DB2 for i can be of enormous help for Web-enabled applications, even when all of the data in the database is stored in a pure relational model.
Consider this example:
Suppose I have an SQL table that I created with the following SQL statement:
create table orders(
order_id bigint generated always as identity
(start with 1000 increment by 1),
cust_email varchar(255),
order_ts timestamp,
product varchar(255),
primary key (order_id));
And assume that I’ve inserted the following rows:
insert into orders (cust_email, order_ts, product)
values
('[email protected]',
'2012-04-15 13:00:00',
'Camera'),
('[email protected]',
'2012-04-16 12:00:00',
'lens'),
('[email protected]',
'2012-04-01 11:00:00',
'Book'),
('[email protected]',
'2012-04-15 13:05:00',
'Book') ;
My application will receive a request from the Web (in XML format) that contains a customer email and a date range. The application is expected to query the table and return matching orders in an XML format.
For simplicity, let’s suppose I’ve retrieved my request XML document from the Web and stored it in a global variable:
create or replace variable order_request xml;
set order_example.order_request = xmlparse(document
'<OrderInfoRequest>
<CustEmail>[email protected]</CustEmail>
<MinTs> 2012-04-14T00:00:00 </MinTs>
<MaxTs> 2012-04-30T23:59:59 </MaxTs>
</OrderInfoRequest>' );
For my sample data and the request above, let’s assume a valid response needs to be structured like this:
<?xml version="1.0" encoding="UTF-8"?>
<InfoRequestResponse>
<MatchingOrder>
<OrderId>1000</OrderId>
<CustEmail>[email protected]</CustEmail>
<OrderTs>2012-04-15T13:00:00.000000</OrderTs>
<Product>Camera</Product>
</MatchingOrder>
<MatchingOrder>
<OrderId>1001</OrderId>
<CustEmail>[email protected]</CustEmail>
<OrderTs>2012-04-16T12:00:00.000000</OrderTs>
<Product>lens</Product>
</MatchingOrder>
</InfoRequestResponse>
Without the SQL/XML support in 7.1, this could be a complex task that would involve both an SQL query and some external code to deal with the XML aspects. However, this can now be accomplished with a single SQL query.
The first thing to understand is that we can join the SQL table to a result set generated by an XMLTABLE invocation.
The SQL query that follows returns only the rows that match the specifications within the provided XML OrderInfoRequest document:
select ORDER_ID,
CUST_EMAIL,
ORDER_TS,
PRODUCT
from
orders,
xmltable('OrderInfoRequest'
passing order_example.order_request
columns "CustEmail" varchar(255),
"MinTs" Timestamp,
"MaxTs" Timestamp
) info_request
where
orders.cust_email = info_request."CustEmail" and
orders.order_ts >= info_request."MinTs" and
orders.order_ts <= info_request."MaxTs";
Creating the XML response document can be done by combining the above query with SQL publishing functions. Each of the rows generated by the above query must be converted into “MatchingOrder” elements. The rest of the document must then be constructed around those values.
This might seem complicated, but the trick is to construct the document “inside out,” using common table expressions and arrogations to create the inner values that are repeated.
The following query is all we need to satisfy the application’s requirement.
-- intermediate result set of
-- “MatchingOrder” Elements
with matching_orders as (
select
XMLELEMENT(NAME "MatchingOrder",
XMLFOREST(ORDER_ID AS "OrderId",
CUST_EMAIL AS "CustEmail",
ORDER_TS AS "OrderTs",
PRODUCT AS "Product")
) AS ORDER
from orders,
xmltable('OrderInfoRequest'
passing order_example.order_request
columns "CustEmail" varchar(255),
"MinTs" Timestamp,
“MaxTs" Timestamp
) info_request
Where
orders.cust_email = info_request."CustEmail" and
orders.order_ts >= info_request."MinTs" and
orders.order_ts <= info_request."MaxTs"
)
-- build InfoRequestResponse element
-- around matching orders
select
XMLSERIALIZE(
XMLDOCUMENT(
XMLELEMENT(NAME "InfoRequestResponse",
XMLAGG(matching_orders.ORDER)
)
) AS CLOB(1G) CCSID 1208 INCLUDING XMLDECLARATION
)
from matching_orders;
You may have noticed that the XML xs:dateTime is not a valid lexical format for an SQL timestamp (and vice versa). This is an annoying problem for hand-written solutions, but the SQL/XML functions handle the conversions between the XML types and SQL types automatically for us. Designing the code with SQL/XML ensures that the result is well-formed XML.
The learning curve for using these XML functions can be a little steep. But in the long run, using this support will make the process of modernizing an application to incorporate Web technologies easier. Avoiding the need to write special purpose code to include XML data in your SQL queries makes it easier to adapt and expand the scope of the application as the Web-facing components evolve.
For a more general overview on the XML data model and what it means to DB2, check out these recently published articles:
http://www.ibmsystemsmag.com/ibmi/developer/general/xml_db2_part1/
http://www.ibmsystemsmag.com/ibmi/developer/general/xml_db2_part2/
Announcement for XMLTABLE:
Comments