This section compares SQL syntax with XQuery syntax in order to give readers already familiar with SQL a jumpstart on learning XQuery. If you notice similarities between SQL and XQuery, it is not a coincidence; some of the key developers of the SQL standard also worked on the XQuery standard. Not all SQL syntax is covered in this chapter, only the most commonly used constructs.
To compare SQL and XQuery queries, will we first start with our simple product catalog document. A basic SQL query might select all the values from the table that meet some specific criteria, for example those in the ACC department. The SQL statement that accomplishes this is:
select * from catalog where dept='ACC'
In XQuery, we can use a straight path expression for such a simple query, as in:
doc("catalog2.xml")//product[@dept='ACC']
If you don't want to sort your results or construct new elements, it is often simpler (and possibly faster) to just use a path expression. However, we could also use a full FLWOR expression that uses a where
clause similar to SQL, as in:
for $prod in doc("catalog2.xml")//product where $prod/@dept='ACC' return $prod
In the where
clause, we need to start the reference to the dept
attribute with $prod/
in order to give it some context. This is different from SQL, where if there is only one dept
column in the table(s) in the query, it is assumed to be that one column. In XQuery, you must be explicit about where the dept
attribute appears, because it could appear on any level of the document.
Now, suppose we want to sort the values by the product number. In SQL, we would simply add an order by
clause, as in:
select * from catalog
where dept='ACC'
order by number
We would also add an order by
clause to the FLWOR, again giving it the context, as in:
for $prod in doc("catalog2.xml")//product
where $prod/@dept='ACC'
order by $prod/number
return $prod
Conditions and operators are used to filter query results. Many of the conditions and operators available in SQL can also be used in XQuery, although sometimes with a slightly modified syntax.
The example in the previous section used the equals sign (=) to compare the value of the department to ACC. XQuery has the same comparison operators as SQL, namely =, !=, <, <=, >, and >=. The BETWEEN condition in SQL is not directly supported, but you can always use two comparisons, as in:
for $prod in doc("catalog2.xml")//product where $prod/number > 500 and $prod/number < 700 return $prod
Like SQL, quotes are used to surround string values, whereas they are not used to surround numeric values.
Strings in SQL can also be matched to wildcards using a LIKE condition. For example, the query:
select * from catalog where name LIKE 'F%'
will return products whose names start with the letter F. XQuery provides a starts-with
function that would be useful in this particular case. For example:
for $prod in doc("catalog2.xml")//product where starts-with($prod/name,'F') return $prod
For the more general case, the matches
function allows you to match a string against any regular expression. In regular expressions, a single period (.) represents any one character, much like the underscore character (_) in LIKE conditions. Adding an asterisk after the period (.*) allows any number of any characters, similar to the % character in LIKE conditions. Anchors (^ and $) can be used indicate the start and end of a string. Another way of expressing the previous query is:
for $prod in doc("catalog2.xml")//product where matches($prod/name,'^F.*') return $prod
Regular expressions are much more powerful than LIKE conditions, though (see Table 24-4). They are discussed in detail in Chapter 18.
Table 24-4. LIKE condition values versus regular expressions
Like clause |
Equivalent regular expression |
Examples of matching values |
---|---|---|
xyz |
^xyz$ |
xyz |
xyz_ |
^xyz.$ |
xyza |
xyz% |
^xyz.*$ |
xyz, xyza, xyzaaa |
_xyz |
^.xyz$ |
axyz |
xyz% |
^xyz |
xyz, xyza, xyzaa |
%xyz |
xyz$ |
xyz, axyz, aaxyz |
x_yz |
^x.yz$ |
xayz |
x%yz |
^x.*yz$ |
xyz, xayz, xaayz |
The IN condition in SQL is useful for comparing a value with a list of specified values. For example, to find all the products that are in either the ACC or WMN departments, you could use the query:
select * from catalog
where dept in
('ACC', 'WMN')
In XQuery, no special "in" operator is needed because the equals operator (=) allows multiple values on either side of the comparison, as in:
for $prod in doc("catalog2.xml")//product
where $prod/@dept =
('ACC', 'WMN')
return $prod
The meaning of the = operator in this case is that the product department must be equal to at least one of the values ACC or WMN.
For arithmetic operations, XQuery uses +, −, and × for addition, subtraction, and multiplication, just like SQL. For division, XQuery does not support the / operator (because that's needed in path expressions), but instead uses div
and idiv
operators. These are covered in detail in "Arithmetic Operations" in Chapter 16.
In SQL, multiple conditions are often combined with and
and or
operators, and sometimes parentheses are used to group conditions together. For example, the following query selects the products that are in the ACC department whose names start with either F or G:
select * from catalog where dept='ACC'and
(
name like 'F%'or
name like 'G%')
Parentheses are used around the name-related conditions to prevent the and
operator from taking precedence. If the parentheses were not there, the first two conditions would be "and-ed" together, with different results.
The and
and or
operators, and the parentheses, work identically in XQuery. An equivalent XQuery query is:
for $prod in doc("catalog2.xml")//product where $prod/@dept='ACC'and
(
matches($prod/name,'F.*')or
matches($prod/name,'G.*'))
return $prod
To negate a condition in SQL, you can use a not
operator, which is sometimes applied to a parenthesized group, and is sometimes part of the comparison syntax, as in name
not
like 'F%'
. In XQuery, you use a not
function, so it is always followed by an expression in parentheses. For example, the SQL query:
select * from catalog wherenot(
dept='ACC')
and (namenot
like 'F%')
is equivalent to the following XQuery query:
for $prod in doc("catalog2.xml")//product wherenot(
$prod/@dept='ACC')
andnot(
matches($prod/name,'F.*'))
return $prod
SQL has a number of built-in functions, many of which have equivalent XQuery functions, often with the same name. Some of the commonly used SQL functions and their XQuery equivalents are listed in Table 24-5. The syntax to call functions is the same in both languages: the arguments are enclosed in parentheses and separated by commas.
Table 24-5. Equivalent functions
SQL function |
XQuery function |
---|---|
Numeric functions | |
sum |
sum |
avg |
avg |
count |
count |
max |
max |
min |
min |
round |
round |
ceil |
ceiling |
floor |
floor |
String functions | |
substr |
substring |
concat |
concat |
upper |
upper-case |
lower |
lower-case |
trim |
normalize-space |
replace |
replace |
length |
string-length |
Date-related functions | |
current_date |
current-date |
current_timestamp |
current-dateTime |
SQL has a DISTINCT
keyword that allows only distinct values to be selected. For example, to get a list of the unique departments in the catalog, you would use the query:
select distinct dept from catalog
In XQuery, you would make a call to the distinct-values
function, as in:
distinct-values(doc("catalog2.xml")//product/@dept)
or, if you prefer a FLWOR:
for $value in distinct-values(doc("catalog2.xml")//product/@dept) return $value
Often you are interested in a combination of distinct values. In SQL, this is quite straightforward; you simply add more columns to the query. To get the distinct combinations of department and product number, you could use:
select distinct dept, number from catalog
However, the XQuery distinct-values
function only accepts one set of values. This means that you must use a FLWOR expression with multiple for
clauses to achieve the same result. This is shown in Example 24-2 and described further in "Selecting Distinct Values" in Chapter 6.
Example 24-2. Distinctness on a combination of values
Query for $d in distinct-values(doc("catalog2.xml")//product/@dept), $n in distinct-values(doc("catalog2.xml")//product[@dept = $d]/number) return <result dept="{$d}" number="{$n}"/> Results <result dept="WMN" number="557"/> <result dept="ACC" number="563"/> <result dept="ACC" number="443"/> <result dept="MEN" number="784"/>
Many SQL queries join multiple tables together. For example, suppose you want to join the order and product tables to retrieve results that contain values from both tables. In SQL, you might write a query such as:
select order_item.num, catalog.name, order_item.quantity from order_item, catalog where order_item.num = catalog.number
In XQuery, joins are not needed so frequently because the data will often be stored in a single hierarchy rather than being split across multiple tables. However, joins still arise and the mechanism is similar. In XQuery, the join in the where
clause might look like Example 24-3.
Example 24-3. Two-way join in XQuery
Query
for $item in doc("order.xml")//item,
$product in doc("catalog2.xml")//product
where $item/@num = $product/number
return <item num="{$item/@num}"
name="{$product/name}"
quan="{$item/@quantity}"/>
Results
<item num="557" name="Fleece Pullover" quan="1"/>
<item num="563" name="Floppy Sun Hat" quan="1"/>
<item num="443" name="Deluxe Travel Bag" quan="2"/>
<item num="784" name="Cotton Dress Shirt" quan="1"/>
<item num="784" name="Cotton Dress Shirt" quan="1"/>
<item num="557" name="Fleece Pullover" quan="1"/>
The XQuery example constructs an item
element to hold each set of three values. This is because XQuery by default does not return the result of each evaluation of the return
clause in a "row" or any other container. If you simply returned the three values for each product, as in:
return ($item/@num, $product/name,$item/@quantity)
the result would be 18 sequential values (557
, Fleece Pullover
, 1
, 563
, Floppy Sun Hat
, etc.), with no relationship among them. The item
element serves as a container to group the related three values together—the same purpose a row would serve in an SQL result.
As an alternative to a where
clause, you can use one or more predicates in your for
clause, as in:
for $item in doc("order.xml")//item,
$product in doc("catalog2.xml")//product[number = $item/@num]
return <item num="{$item/@num}"
name="{$product/name}"
quan="{$item/@quantity}"/>
More information on joins in XQuery can be found in"Joins" in Chapter 6, including examples of three-way joins and outer joins.
Another way to use multiple tables in SQL (or, indeed, multiple queries on the same table) is using subselects. Suppose we wanted to return all products from the catalog that are included in a particular order. We might use the following query:
select * from catalog where number in (select num from order_item where ordnum = '00299432')
Like SQL select statements, XQuery FLWOR expressions can also be contained within each other. The following query uses a FLWOR embedded in the where
clause.
for $product in doc("catalog2.xml")//product where $product/number = (for $item in doc("order.xml")/order[@num='00299432']/item return $item/@num) return $product
In fact, XQuery allows expressions to be nested more freely than SQL does. For example, you can use a nested FLWOR expression in the in
clause, or in the return
clause.
SQL supports the use of set operators such as UNION to combine the rows from multiple select statements. These set operators have equivalents in XQuery, as shown in Table 24-6.
In SQL, it is straightforward to group data by certain values. For example, if you want to count the number of products in each department, you can use the query:
select dept, count(*) from catalog group by dept
XQuery does not have an explicit grouping syntax, but grouping can be achieved using FLWOR expressions and the distinct-values
function. Example 24-4 is comparable to the SQL example.
Example 24-4. Grouping in XQuery
Query for $d in distinct-values(doc("catalog2.xml")//product/@dept) return <result dept="{$d}" count="{count(doc("catalog2.xml")//product[@dept = $d])}"/> Results <result code="ACC" count="2"/> <result code="MEN" count="1"/> <result code="WMN" count="1"/>
As you can see, in order to construct a "table" of return values, we construct result
elements with two attributes representing the "columns." Grouping is covered in more detail in Chapter 7.
18.216.151.164