Tải bản đầy đủ (.pdf) (89 trang)

Building Oracle XML Applications phần 5 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (489.11 KB, 89 trang )

SELECT *
FROM doctor
WHERE id = '{@id}'

</xsql:query>
This parameterized XSQL page can be used to retrieve information about doctors, given the value
of their email id in the requesting URL like this:
http://xmlapps/examples/DoctorInfo.xsql?id=bproto
which returns the datagram:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<ID>bproto</ID>
<FIRSTNAME>Bryan</FIRSTNAME>
<LASTNAME>Proto</LASTNAME>
<HOMEOFFICE>French44</HOMEOFFICE>
</ROW>
</ROWSET>
Parameter references like {@paramname} in an XSQL page's <xsql:query> are substituted with
their appropriate parameter values from the query string whenever the page is requested. This
means you can build a page that uses parameters to provide parts of the query statement, like
this:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" select="*" where="1=1" order="1"
xmlns:xsql="urn:oracle-xsql">

SELECT {@select}
FROM {@from}
WHERE {@where}
ORDER BY {@order}


</xsql:query>
This XSQL page allows the requester to control all the parts of the syntax of the SELECT
statement. You can use XML attributes with the same names as the parameters to provide
defaults in case the requester does not provide a specific value.
Taking the last example a step further, you can build a page that uses a parameter to represent
the entire SQL statement:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">

SELECT *
FROM ( {@sql} )

</xsql:query>
Such a page allows any requester with the authorization to access the page the ability to return
XML datagrams for any SQL statement on your server. This obviously is not something you would
want to use on a production system without appropriate authentication. Since XSQL pages can be
secured using standard web page security mechanisms, this access can be set up so it is not
abused.
8.1.4 Customizing the XSQL Results
The most basic way to get XML results from queries into your XSQL page is to include the text of
the desired SQL query between the <xsql:query> and </xsql:query> tags. Using this technique,
each query in the page will produce the following structure by default:
• A <ROWSET> element containing the query's results
• A <ROW> element and a row identifier, for each row in the result set
• A child element for each non-NULL column in each <ROW>
The element names for each column in the result set are derived from the column names or
column aliases if provided.
8.1.4.1 Changing element names by aliasing columns
You can use the standard SQL columnname AS alias syntax to rename a column included in the
SELECT list or to specifically provide a column alias for a SQL expression like this:

<?xml version="1.0"?>
<xsql:query connection="scott" xmlns:xsql="urn:oracle-xsql">

SELECT dept.deptno AS department,
SUM(sal) AS "TOTAL-SALARIES"
FROM emp,dept
WHERE dept.deptno = emp.deptno
GROUP BY dept.deptno
ORDER BY 1

</xsql:query>
If you include the column alias in double quotes, the alias will be used verbatim; otherwise,
names are converted to uppercase. In this case, the double quotes are required, since a name
with a dash in it, like TOTAL-SALARIES in the following example, is not a valid SQL column name:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<DEPARTMENT>10</DEPARTMENT>
<TOTAL-SALARIES>8750</TOTAL-SALARIES>
</ROW>
<ROW num="2">
<DEPARTMENT>20</DEPARTMENT>
<TOTAL-SALARIES>10875</TOTAL-SALARIES>
</ROW>
<ROW num="3">
<DEPARTMENT>30</DEPARTMENT>
<TOTAL-SALARIES>9400</TOTAL-SALARIES>
</ROW>
</ROWSET>
8.1.4.2 Tailoring XML query results

In addition to using column aliasing and parameter values to control the query's XML output, you
can also specify values for any combination of the <xsql:query> tag attributes in Table 8.1. This
allows you to further refine each query's XML results.
Table 8.1. xsql:query Attributes to Control XML Output
Attribute Name Description
rowset-element
Element name to use as a parent for the rowset of query results. The
default name is <ROWSET>. Set equal to the empty string to suppress
printing an element for the rowset.
row-element
Element name to use as a parent element for each row in the query
results. The default name is <ROW>. Set equal to the empty string to
suppress printing a row element.
max-rows
Maximum number of rows to fetch from the query. Useful in combination
with an ORDER BY in your query for fetching the top-N rows. When used
in combination with skip-rows, it's easy to implement getting the next-N
rows from a query result to page through a set of query results. The
default is to fetch all rows.
skip-rows
Number of rows to skip over before returning the query results. The
default is not to skip any rows.
id-attribute
Attribute name for the row identifier attribute for each row in the query
result. The default is num.
id-attribute-column

Column name to use to supply the value of the row identifier attribute for
each row in the query result. The default is to use the row count as the
value.

null-indicator
If set to y or yes, causes a null-indicator attribute to be used on the
element for any column whose value is NULL. The default is to omit the
element in the result for any column with a NULL value.
tag-case
If set to upper, causes the element names for columns in the query result
to be in uppercase. If set to lower, causes the element names for
columns in the query result to be in lowercase. The default is to use the

case of the column name (or column aliases if provided) from the query.
fetch-size
Explicitly sets the number of records retrieved in each round-trip to the
database for this query. This overrides the default fetch size set in the
XSQL configuration file.
For example, using rowset-element and row-element you can serve a datagram for a list of
department numbers and a sum of all the salaries in each department as a <DEPARTMENT-LIST>
document with nested <DEPARTMENT> elements. The XSQL page for this looks like the following:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" xmlns:xsql="urn:oracle-xsql"
rowset-element="DEPARTMENT-LIST" row-element="DEPARTMENT">

SELECT dept.deptno AS "NUMBER",
sum(sal) AS "TOTAL-SALARIES"
FROM emp,dept
WHERE dept.deptno = emp.deptno
GROUP BY dept.deptno
ORDER BY 1

</xsql:query>
Requesting this page produces the result:

<?xml version = '1.0'?>
<DEPARTMENT-LIST>
<DEPARTMENT num="1">
<NUMBER>10</NUMBER>
<TOTAL-SALARIES>8750</TOTAL-SALARIES>
</DEPARTMENT>
<DEPARTMENT num="2">
<NUMBER>20</NUMBER>
<TOTAL-SALARIES>10875</TOTAL-SALARIES>
</DEPARTMENT>
<DEPARTMENT num="3">
<NUMBER>30</NUMBER>
<TOTAL-SALARIES>9400</TOTAL-SALARIES>
</DEPARTMENT>
</DEPARTMENT-LIST>
To suppress the row identifier attribute, and force all of the XML element names to lowercase, we
can add the id-attribute="" and tag-case="lower" attributes:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" id-attribute="" tag-case="lower"
rowset-element="DEPARTMENT-LIST" row-element="DEPARTMENT"
xmlns:xsql="urn:oracle-xsql">

SELECT dept.deptno AS "NUMBER",
sum(sal) AS "TOTAL-SALARIES"
FROM emp,dept
WHERE dept.deptno = emp.deptno
GROUP BY dept.deptno
ORDER BY 1

</xsql:query>

This produces the expected changes in the resulting datagram:
<?xml version = '1.0'?>
<department-list>
<department>
<number>10</number>
<total-salaries>8750</total-salaries>
</department>
<department>
<number>20</number>
<total-salaries>10875</total-salaries>
</department>
<department>
<number>30</number>
<total-salaries>9400</total-salaries>
</department>
</department-list>
You may set either or both rowset-element and row-element to the empty string to suppress
their output. Doing so can be useful when you know that the results of the query will produce a
single row or a single column and you don't need the extra levels of <rowset> and <row> elements
in the resulting document. As an example, the following XSQL page:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" id-attribute="" xmlns:xsql="urn:oracle-xsql"
rowset-element="" row-element="">

SELECT sum(sal) AS "TOTAL"
FROM emp,dept
WHERE dept.deptno = emp.deptno
AND dept.deptno = {@dept}
GROUP BY dept.deptno
ORDER BY 1


</xsql:query>
produces this one-element document containing only the <TOTAL> tag generated for the TOTAL
column in the results:
<?xml version = '1.0'?>
<TOTAL>10875</TOTAL>
When suppressing rowset-element or row-element in an XSQL page comprised of only a single
query tag, you must ensure that your results still generate a well-formed XML document. For
example, this XSQL page, which specifies both rowset-element="" and row-element="":
<?xml version="1.0"?>
<xsql:query connection="xmlbook" rowset-element="" row-element=""
xmlns:xsql="urn:oracle-xsql">

/* This query returns one row, two columns */

SELECT 1 AS VALUE1, 2 AS VALUE2 FROM DUAL

</xsql:query>
would generate the following result:
<?xml version = '1.0'?>
<VALUE1>1</VALUE1>
<VALUE2>2</VALUE2>
This is not a well-formed XML document, since it has two top-level elements instead of the single
document element required by the XML specification. Similarly, an example like this:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" rowset-element="" row-element="PERSON"
xmlns:xsql="urn:oracle-xsql">

/* This query returns two rows of one column each */


SELECT 'Emma' AS NAME FROM DUAL
UNION
SELECT 'Amina' AS NAME FROM DUAL

</xsql:query>
returns two rows but suppresses the rowset-element:
<?xml version = '1.0'?>
<PERSON>
<NAME>Emma</NAME>
</PERSON>
<PERSON>
<NAME>Amina</NAME>
</PERSON>
This again, is not well-formed XML. In both cases, rather than returning an invalid page, the XSQL
page processor will return an error:
Oracle XSQL Command Line page processor
XSQL-014: Resulting page is an empty document or had multiple document elements.
If you are building a page with a query that you think should return a single row, you can
guarantee that only a single row will be returned by using the max-rows="1" attribute on your
query tag to avoid this situation.
8.1.4.3 Using parameters in <xsql:query> tag attributes
In addition to using parameters in the SQL statement for an <xsql:query> tag, the values of
<xsql:query> tag attributes can also reference parameters. This makes it easy for the requester
to pass in values for the attributes that control the query's XML output. For example, a page like
this:
<?xml version="1.0"?>
<xsql:query connection="xmlbook" cat="news" skip="0"
max-rows="2" skip-rows="{@skip}" xmlns:xsql="urn:oracle-xsql">

SELECT title,url

FROM site_entry
WHERE categories like '%'||UPPER('{@cat}')||'%'
ORDER BY id desc

</xsql:query>
uses the following <xsql:query> tag attributes:
cat="news"
Specifies news as the default category for requested headlines
skip="0"
Defaults the value of the skip parameter to zero
max-rows="2"
Shows at most two rows of results
skip-rows="{@skip}"
Skips the number of rows specified in the skip parameter before returning data
So the following URL:
http://xmlapps/examples/BrowseHeadlines.xsql
produces an XML datagram showing the first two stories from the news category:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<TITLE>New XML Content on Oracle TechNet</TITLE>
<URL>
</ROW>
<ROW num="2">
<TITLE>XML.org Premieres As Repository For Schemas</TITLE>
<URL>
</ROW>
</ROWSET>
If we add the URL parameter skip=2, then the skip-rows="{@skip}" attribute on the query tag
will evaluate to 2, overriding the default value of zero. So the result of requesting:

http://xmlapps/examples/BrowseHeadlines.xsql?skip=2
is the XML datagram showing just the third and fourth rows of the results:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="3">
<TITLE>Microsoft's Maritz at TechEd on XML</TITLE>

<URL>
</ROW>
<ROW num="4">
<TITLE>IBM XML Translator Generator</TITLE>
<URL>
</ROW>
</ROWSET>
Providing a value for the cat parameter in the URL allows the requester to request a different
category of stories. The following request:
http://xmlapps/examples/BrowseHeadlines.xsql?cat=software&skip=4
shows the fifth and sixth news stories in the software category:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="5">
<TITLE>Beta 1.5 of Microsoft XML Notepad</TITLE>
<URL>
</ROW>
<ROW num="6">
<TITLE>SAXON 4.2 Released</TITLE>
<URL>
</ROW>
</ROWSET>
Here we've seen parameters in use for attribute values to control the behavior of individual

<xsql:query> tags, but as a final note to this section, we recall that it's also possible to use a
parameter to control the page-level connection. By simply including a parameter in the
connection attribute on the document element, you can build a page that allows the requester to
provide the name of the named connection as a parameter to the request. So, with a page that
begins like this:
<page xmlns:xsql="urn:oracle-xsql" connection="{@conn}">
the requester can provide a parameter like conn="prod" or conn="test" to select between the
production database whose named definition is prod and the test database instance whose
named definition is test. Using the techniques we learned earlier, a default value for the conn
attribute can be supplied using an attribute of that name on the document element, like this:
<page xmlns:xsql="urn:oracle-xsql" conn="prod" connection="{@conn}">
This causes the page to default to the connection named prod, but allows the conn="test"
parameter to be supplied to override the default value and use the test database connection for
a particular request.
8.1.5 Using Multiple Queries on a Page
Our examples so far have all used a single <xsql:query> tag as the only element in the XSQL
page. However, there is no limit to the number of queries that can appear in a page or to where
the <xsql:query> tags can appear in the page. The only overriding rule is that the XSQL page
itself must be a well-formed XML document: an <xsql:query> tag can appear anywhere an
element can appear in a document:
<?xml version="1.0"?>
<a-query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
<can-go-here/>
<xsql:query> SELECT * FROM table1 </xsql:query>
<or-here>
<xsql:query> SELECT * FROM table2 </xsql:query>
</or-here>
Or even <xsql:query> SELECT * FROM table3 </xsql:query> here.
</a-query>
Conversely, anywhere it would be illegal to put an XML element in a well-formed document is an

illegal place for an <xsql:query> tag. The following page shows three illegal places for an
<xsql:query> tag to appear:
<?xml version="1.0"?>
<a-query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
<cannot go-here="<xsql:query> SELECT * FROM table1 </xsql:query>"/>
<or-here<xsql:query> SELECT * FROM table2</xsql:query>/>
<!
Ok here, but has no effect in a comment
<xsql:query> SELECT * FROM table3 </xsql:query>
>
</a-query>
<but-not-here>
<xsql:query> SELECT * FROM table4 </xsql:query>
</but-not-here>
An <xsql:query> tag cannot appear:
• Inside the value of an XML attribute
• As part of an element or attribute name.
• Outside the document element
Of course it's legal, even useful sometimes, to include an <xsql:query> tag inside a comment
between <! and > in the page, but when it's inside a comment it won't be executed by the
XSQL page processor.
Example 8.3
shows an XSQL page for returning a client's stock portfolio information; it includes
<xsql:query> tags wherever we need dynamic output inserted.
Example 8.3. Portfolio of Quotes Using Multiple Queries
<?xml version="1.0"?>
<client-portfolio connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
<xsql:query rowset-element="" row-element="">

select to_char(sysdate,'DD-MON-YYYY') as "effective-date" from dual


</xsql:query>
<client>
<info>
<xsql:query rowset-element="" row-element="" id-attribute=""
tag-case="lower">

SELECT id, name, homeaddress, workaddress
FROM clients
WHERE id = {@id}

</xsql:query>
</info>
</client>
<xsql:query rowset-element="portfolio" row-element="quote" tag-case="lower" >

SELECT q.symbol,
q.price,
q.change
FROM quotes q, portfolio_stocks ps
WHERE q.symbol = ps.symbol
AND ps.owner = {@id}

</xsql:query>
</client-portfolio>
Let's examine a few things that make this XSQL page different from those we've seen previously:
• The page starts with the <client-portfolio> element instead of an <xsql:query> tag.
• Since it must appear on the document element of the page, the connection="xmlbook"
appears on the <client-portfolio> element instead of on any individual <xsql:query>
element.

• The page uses three <xsql:query> tags instead of just a single one.
• The second <xsql:query> tag appears nested inside the static elements <client> and
<info>.
• Two different queries in the page both refer to the {@id} parameter.
Requesting this page with the URL:
http://xmlapps/examples/ ClientPortfolio.xsql?id=101
produces the output shown in Example 8.4
.
Example 8.4. Results of an XSQL Page with Multiple Queries
<?xml version = '1.0'?>
<client-portfolio>
<effective-date>26-SEP-1999</effective-date>
<client>
<info>
<id>101</id>
<name>
<givenname>Tim</givenname>
<familyname>Jones</familyname>
</name>
<homeaddress>
<street>301 Ruxpin Road</street>
<city>Palo Alto</city>
<state>CA</state>
<postcode>94301</postcode>
<country>USA</country>
</homeaddress>
<workaddress>
<street>12 Infinite Loop</street>
<city>Cupertino</city>
<state>CA</state>

<postcode>95014</postcode>
<country>USA</country>
</workaddress>
</info>
</client>
<portfolio>
<quote num="1">
<symbol>GE</symbol>
<price>103.50</price>
<change>0.80</change>
</quote>
<quote num="2">
<symbol>ORCL</symbol>
<price>27.33</price>
<change>3.40</change>
</quote>
</portfolio>
</client-portfolio>
This example is worth examining a little more closely. The first query uses rowset-element=""
and row-element="" to produce a single <effective-date> element in the result as a child
element of the <client-portfolio> element. This technique can be used anywhere a single,
dynamic element is useful.
The second query in the page produces an additional, nested element structure for the <name>,
<homeaddress>, and <workaddress> elements in the result. This is an example of what
automatically occurs when the datatype for a column is a user-defined object type, rather than
one of the simple scalar datatypes like VARCHAR2, DATE, or NUMBER.
In this case, since we define the clients table as:
CREATE TABLE clients (
id NUMBER PRIMARY KEY,
name PERSON_NAME,

homeaddress ADDRESS,
workaddress ADDRESS
);
the user-defined PERSON_NAME and ADDRESS datatypes are being used and the results
automatically reflect the nested structure of their attributes.
If we come up with a simple query over the Oracle Data Dictionary views USER_TAB_COLUMNS
and USER_TYPE_ATTRS, we can quickly build an XSQL page like the one in Example 8.5
to
examine the structure of any table or type in our schema interactively.
Example 8.5. Describe.xsql Explores the Oracle Data
Dictionary

<?xml version="1.0"?>
<xsql:query connection="xmlbook" rowset-element="COLUMNS" row-element="COLUMN"
tag-case="lower" id-attribute="" xmlns:xsql="urn:oracle-xsql">

SELECT column_id as id, column_name as name, data_type as "TYPE"
FROM user_tab_columns
WHERE table_name = UPPER('{@obj}')
UNION
SELECT attr_no as id, attr_name as name, attr_type_name as "TYPE"
FROM user_type_attrs
WHERE type_name = UPPER('{@obj}')
ORDER BY id

</xsql:query>
We can use this page to confirm our theory about why the extra nested structure appeared in the
query over the clients table. By requesting the page with the URL parameter of obj=clients, like
this:
http://xmlapps/examples/Describe.xsql?obj=clients

as shown in Example 8.6
, we'll immediately see a list of the columns and datatypes for the clients
table.
Example 8.6. Column Information for Table with Object-Type
Columns

<?xml version = '1.0'?>
<columns>
<column>
<id>1</id>
<name>ID</name>
<type>NUMBER</type>
</column>
<column>
<id>2</id>
<name>NAME</name>
<type>PERSON_NAME</type>
</column>
<column>
<id>3</id>
<name>HOMEADDRESS</name>
<type>ADDRESS</type>
</column>
<column>
<id>4</id>
<name>WORKADDRESS</name>
<type>ADDRESS</type>
</column>
</columns>
We can see that the results are as we expected them to be: the user-defined type names appear

as the datatypes for the columns NAME, HOMEADDRESS, and WORKADDRESS. Repeating the request to
examine the structure of the ADDRESS type:
http://xmlapps/examples/Describe.xsql?obj=address
produces the results shown in Example 8.7
.
Example 8.7. Querying the Data Dictionary for Column-Type
Information

<?xml version = '1.0'?>
<columns>
<column>
<id>1</id>
<name>STREET</name>
<type>VARCHAR2</type>
</column>
<column>
<id>2</id>
<name>CITY</name>
<type>VARCHAR2</type>
</column>
<column>
<id>3</id>
<name>STATE</name>
<type>VARCHAR2</type>
</column>
<column>
<id>4</id>
<name>POSTCODE</name>
<type>VARCHAR2</type>
</column>

<column>
<id>5</id>
<name>COUNTRY</name>
<type>VARCHAR2</type>
</column>
</columns>
Here we see that the ADDRESS type has been defined to have attributes STREET, CITY, STATE,
POSTCODE, and COUNTRY, all of type VARCHAR2. Note that the query in the Describe.xsql page
used the SQL UNION operator to select information from a combination of the
USER_TAB_COLUMNS and USER_TYPE_ATTRS tables. Since a table and a type cannot both have
the same name, for any given value of the obj parameter passed our page, the query will return
either table information or type information, but not both.
8.1.6 Producing XML from SQL with Nested Structure
Let's look more closely at the different ways to produce XML with nested structure.Structured
columns can be one of three types:
• Strongly typed, user-defined object
• Strongly typed, user-defined collection
• Untyped collection based on a SQL statement
The underlying Oracle XML SQL Utility for Java natively supports producing richly structured XML
from SQL statements that make use of these types, so your Oracle XSQL Pages has this capability.
We'll look at two simple examples.
8.1.6.1 Using user-defined object types
Suppose that you have used the object/relational capabilities of Oracle8i to create a user-defined
object type called POINT using the command:
CREATE TYPE POINT AS OBJECT (X NUMBER, Y NUMBER);
and have used your new POINT type as the datatype of the ORIGIN column in your LOCATION
table with the following DDL statement:
CREATE TABLE LOCATION (
NAME VARCHAR2(80),
ORIGIN POINT

);
Suppose further that you have inserted a row into this LOCATION table using an INSERT
statement with the POINT( ) constructor, like this:
INSERT INTO LOCATION VALUES ( 'Someplace', POINT(11,17) );
COMMIT;
Then you can construct an XSQL page like point.xsql that does a query over the LOCATION table
like this:
<xsql:query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
SELECT name, origin
FROM location loc
WHERE loc.origin.x = {@x-coord}
</xsql:query>
When requested using a URL like:

this page produces the following output:
<ROWSET>
<ROW num="1">
<NAME>Someplace</NAME>
<ORIGIN>
<X>11</X>
<Y>17</Y>
</ORIGIN>
</ROW>
</ROWSET>
This demonstrates how the nested X and Y attributes in the POINT datatype structure of the
ORIGIN column appear automatically as nested <X> and <Y> elements in the XML output. It is
about the simplest example of using a user-defined type to get more richly structured XML output
from your object/relational database. In Chapter 12
, we cover Oracle8 object types more
extensively as we explore how to use them to model the structure of XML documents to be

inserted into the database, and in Chapter 17
, we illustrate how to use object views with nested
collection types to group master/detail information for a news portal we build in that chapter.
8.1.6.2 Using the CURSOR operator for nested rowsets
If you have not created object types that contain a predefined structure, you can still introduce
nested structure into your SQL queries using the CURSOR operator, which allows you to select a
nested rowset as a column in the SELECT list of a query. While almost any nested query is legal
to include inside the CURSOR operator in the SELECT list, the most useful query selects a nested
set of detail rows for the current master row.
Taking the familiar dept and emp tables as an example, the following XSQL page contains a query
that selects the DNAME column from the dept table and, for each row returns a nested rowset of
the EMPLOYEES from the emp table who work in that department:
<xsql:query connection="demo" xmlns:xsql="urn:oracle-xsql">
SELECT dname,
CURSOR( SELECT ename,sal
FROM emp
WHERE emp.deptno = dept.deptno) as employees /* Column Alias */
FROM dept
WHERE deptno = {@department}
</xsql:query>
Requesting this page:

produces the resulting XML data page:
<ROWSET>
<ROW num="1">
<DNAME>ACCOUNTING</DNAME>
<EMPLOYEES>
<EMPLOYEES_ROW num="1">
<ENAME>CLARK</ENAME>
<SAL>2450</SAL>

</EMPLOYEES_ROW>
<EMPLOYEES_ROW num="2">
<ENAME>KING</ENAME>
<SAL>5000</SAL>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num="3">
<ENAME>MILLER</ENAME>
<SAL>1300</SAL>
</EMPLOYEES_ROW>
</EMPLOYEES>
</ROW>
</ROWSET>
Note that the second column in the SELECT statement is the CURSOR( ) expression used to select
the details. Since it is a column like any other, it can be aliased to the column name EMPLOYEES by
using the AS COLUMNALIAS syntax as shown above. Since the EMPLOYEES column is a nested
rowset, it appears as a set of <EMPLOYEES_ROW> elements nested within its parent <ROW>.
Given the following facts, you can quickly see how structured information can be created on the
fly in almost any structure you are looking for:
• One or more CURSOR operators can be used in the SELECT list of any SQL statement.
• One or more CURSOR operators can be used in the SELECT list of SQL statements that
appear inside any CURSOR operator (to any level of nesting).
• Virtually any valid SQL statement (including GROUP BY and ORDER BY, etc.) may appear
with the CURSOR operator.
• Any SQL statement can be included in an <xsql:query> tag in an XSQL page.
This allows you to exploit the processing speed of the database for sorting and grouping instead
of having to rely on slower techniques that would attempt these operations on flat data from
within the XSLT stylesheet.
By using these query techniques in the <xsql:query> tags of an XSQL page, you can combine
master/detail XML data pages with the database sorting and grouping. These database
operations are applied to the data pages by the SQL engine before subsequently applying an XSLT

stylesheet to the resulting data page (as we learned earlier). In this way, we can transform the
resulting data page into any presentation format you need.
8.1.7 Providing a Fallback Query
Under normal circumstances, if an <xsql:query> tag in your XSQL page contains a SELECT
statement that returns no rows when the page is processed, the <xsql:query> tag is simply
replaced by an empty <ROWSET/> element with no <ROW> elements as children. However,
sometimes it is useful to attempt an alternate query as a fallback when the original query
produces no rows.
This most often comes in handy when you want to look up some information by an exact match
on a parameter value in the original query, and then retry a different query which searches for a
fuzzy match using the LIKE operator if no exact match is found. Employing a fallback query in
your XSQL page is easy. For any <xsql:query> tag in your XSQL page, the XSQL page processor
recognizes a nested <xsql:no-rows-query> tag as the fallback query to try in case the parent
query retrieves no rows. All of the attributes available to control the output of the <xsql:query>
tag are equally functional on the nested <xsql:no-rows-query>.
Consider the ValidateAirport.xsql page in Example 8.8. It implements an HTTP-based airport
validation service against a database table named AIRPORT containing the more than 9000
airports on the planet and their International Air Transport Association (IATA) three-letter
abbreviations. The page includes a single <xsql:query> tag that searches case-insensitively for
an airport whose three-letter code exactly matches the value of the {@code} parameter. Nested
inside the outer <xsql:query> is a <xsql:no-rows-query> that searches for any airports whose
description contains the value of the {@code} parameter, case-insensitively using SQL's UPPER
function.
Example 8.8. XSQL Page to Validate Airport Codes
<?xml version="1.0"?>
<xsql:query connection="xmlbook" max-rows="1" rowset-element="Ok"
row-element="Airport" xmlns:xsql="urn:oracle-xsql">

SELECT tla "Code", description "Description"
FROM AIRPORT

WHERE tla = UPPER('{@code}')

<xsql:no-rows-query max-rows="4" rowset-element="Error" row-element="Airport">

SELECT tla "Code", description "Description"
FROM AIRPORT
WHERE UPPER(description) LIKE UPPER('%{@code}%')
ORDER BY 2

</xsql:no-rows-query>
</xsql:query>
Note these highlights in the example page:
• The <xsql:query> sets rowset-element="Ok" while the <xsql:no-rows-query> sets
rowset-element="Error". This allows the returned page to signal the success or failure
of the exact match while also providing additional information on matching airports in
either case.
• The <xsql:query> expects an exact match and specifies max-rows="1" while the
<xsql:no-rows-query> uses a max-rows="4" to return the first four fuzzy matches.
Any software that wants to validate the existence of an airport by code can do so over the Web by
requesting a URL like:
http://xmlapps/examples/ValidateAirport.xsql?code=xml
This attempts to validate the existence of an airport with XML as its three-letter code. Surprisingly,
this request produces an exact match with the result:
<?xml version = '1.0'?>
<Ok>
<Airport num="1">
<Code>XML</Code>
<Description>Minlaton, Sa, Australia</Description>
</Airport>
</Ok>

The 900 residents of this small South Australian town must be quite proud of all the attention
their fine airport is now receiving! An attempt to validate an airport code of good using the same
page:
http://xmlapps/examples/ValidateAirport.xsql?code=good
fails the exact match and falls back to the <xsql:no-rows-query> to produce the result:
<?xml version = '1.0'?>
<Error>
<Airport num="1">
<Code>YGH</Code>
<Description>Ft. Good Hope, Nwt, Canada</Description>
</Airport>
<Airport num="2">
<Code>GNG</Code>
<Description>Gooding, Idaho, Usa</Description>
</Airport>
<Airport num="3">
<Code>GLD</Code>
<Description>Goodland, Kansas, Usa</Description>
</Airport>
<Airport num="4">
<Code>GNU</Code>
<Description>Goodnews Bay, Alaska, Usa</Description>
</Airport>
</Error>
The requesting agent, which may be a Java program using the Oracle XML Parser or a dynamic
HTML page in Internet Explorer 5.0 with its integrated client-side XML parser, can notice that the
document element of the returned XML datagram is <Error> and can proceed to collect the list of
suggested matches for subsequent processing or display to the user. It is worth noting that while
any <xsql:query> can have at most one nested <xsql:no-rows-query>, a
<xsql:no-rows-query> itself can have a nested <xsql:no-rows-query> with no limit on the

depth.
8.2 Transforming XSQL Page Results with XSLT
Since the data page that results from executing the queries in your XSQL page template is an XML
document, and since XSLT offers a robust, declarative mechanism for transforming XML
documents into anything else, we can put the two together to achieve a highly effective one-two
punch:
1. The XSQL page assembles XML information relevant to the task at hand.
2. The XSLT stylesheet transforms it into an appropriate format for delivery.
We'll look at a few examples in this section that illustrate this approach.
8.2.1 Associating a Stylesheet with an XSQL Page
To associate an XSLT stylesheet with an XSQL page, we use the standard technique prescribed by
the W3C (see />), the <?xml-stylesheet?> processing
instruction. It goes after the <?xml version="1.0"?> declaration and before the document
element in your XSQL page. It must include as attributes two key pieces of information required
to identify the stylesheet:
• An href attribute that describes the absolute or relative location of the desired stylesheet
• A type="text/xsl" attribute that indicates the type of stylesheet in use
Including an <?xml-stylesheet?> instruction of type "text/xsl" in your XSQL page causes the
XML document obtained by expanding the XSQL page's <xsql:query> tags to be transformed by
the indicated stylesheet in the server before returning the results to the requester. Only an
<?xml-stylesheet?> instruction with an attribute of type="text/xsl" will be considered for use,
since the XSQL page processor supports only this type of stylesheet.
8.2.2 Using a Stylesheet to Produce HTML
A simple example of an XSQL page that includes a reference to an XSLT stylesheet is the
following:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="Bookmark.xsl"?>
<xsql:query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">

SELECT title, url

FROM bookmark
ORDER BY title

</xsql:query>
This page queries information about web browser bookmarks from the bookmark table, and uses
the following Bookmark.xsl stylesheet to format the results:
<html xsl:version="1.0" xmlns:xsl="
<head>
<title>My Bookmarks</title>
</head>
<body>
<h2>My Bookmarks</h2>
<ul>
<xsl:for-each select="ROWSET/ROW">
<li><a href="{URL}"><xsl:value-of select="TITLE"/></a></li>
</xsl:for-each>
</ul>
</body>
</html>
This means that in response to the web request:
http://xmlapps/examples/Bookmark.xsql
The requester won't get the basic raw data page delivered as an XML datagram:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<TITLE>Free XML Software</TITLE>
<URL>
</ROW>
<ROW num="2">
<TITLE>Oracle Technet XML Page</TITLE>

<URL>
</ROW>
</ROWSET>
Instead, the requester will receive the following, which the browser will display as shown in Figure
8.2:
<html>
<head>
<title>My Bookmarks</title>
</head>
<body>
<h2>My Bookmarks</h2>
<ul>
<li>
<a href="
XMLtools.html">Free XML Software</a>
</li>
<li>
<a href="
Technet XMLPage< a>
</li>
</ul>
</body>
</html>
Figure 8.2. HTML list of bookmarks produced using a
stylesheet


It's easy to change the name of the stylesheet being referenced in the XSQL page to change the
look of the page. If you change the stylesheet being referenced, by modifying the value of the
href attribute in your <?xml-stylesheet?> instruction to refer to a different stylesheet, you can

format the same web bookmark data formatted differently in no time at all. Suppose, for instance,
that you use this stylesheet instead:
<html xsl:version="1.0" xmlns:xsl="
<head>
<link rel="stylesheet" type="text/css" href="Bookmark.css" />
<title>My Bookmarks</title>
</head>
<body>
<h2>My Bookmarks</h2>
<table border="1" cellspacing="0">
<xsl:for-each select="ROWSET/ROW">
<tr>
<td><a href="{URL}"><xsl:value-of select="TITLE"/></a></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
Using an example of the desired HTML page as a template, this XSLT stylesheet:
1. Declares the xsl namespace on the root <html> tag so we can use XSL elements in the
stylesheet to loop over data and plug values into the result page
2. Uses <xsl:for-each select="ROWSET/ROW"> to loop through all the <ROW> elements that
appear as children of the <ROWSET> element, creating an HTML table row and table data
tag for each <ROW> encountered
3. Specifies the attribute value template href="{URL}" on the <a> element to plug the
value of the <URL> element from the current <ROW> into the <xsl:for-each> loop
4. Uses <xsl:value-of select="TITLE"/> to plug in the value of the <TITLE> element from
the current <ROW> as the text for the hyperlink
Note the use of the {URL} syntax. It is an example of an XSLT attribute value template, which
provides the handy shortcut syntax of {XPathExpression} for inserting the value of an XPath

expression into an attribute value in the result document.

The XSLT attribute value syntax of {XPathExpression} is very
similar to the syntax for referring to a parameter in an XSQL
page {@paramname}, but they are used in two different
contexts.

The XSLT stylesheet transforms the XSQL data page into an HTML page that uses tables and has
a linked CSS stylesheet Bookmark.css to be used by the browser:
body { font-family: Garamond }
h2 { color: red }
td { font-size: 12pt ; background: yellow }
Requesting the page now produces the list of bookmarks with a different look.

×