order by clauses

An order by clause in an FLWOR expression specifies the order in which values are to be processed by the return clause.

An order by clause contains one or more ordering specifications. Ordering specifications are used to reorder the tuples of variable bindings that are retained after being filtered by the where clause. The resulting order determines the order in which the return clause is evaluated.

Each ordering specification consists of an expression, which is evaluated to produce an ordering key, and an order modifier, which specifies the sort order (ascending or descending) for the ordering keys. The relative order of two tuples is determined by comparing the values of their ordering keys, working from left to right.

In the following example, an FLWOR expression includes an order by clause that sorts products in descending order based on their price:
SELECT XMLQUERY(
  'declare namespace ipo="http://www.example.com/IPO";
  for $i in $po/ipo:purchaseOrder/items/item
  order by xs:decimal($i/USPrice) descending
  return fn:concat($i/productName, ":US$", $i/USPrice)'
  PASSING PORDER as "po")
FROM PURCHASEORDER
Suppose that the PORDER column of the PURCHASEORDER table contains this data:
<ipo:purchaseOrder
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:ipo="http://www.example.com/IPO"
   orderDate="2008-12-01">
  <shipTo exportCode="1" xsi:type="ipo:UKAddress">
    <name>Helen Zoe</name>
    <street>55 Eden Street</street>
    <city>San Jose</city>
    <state>CA</state>
    <postcode>CB1 1JR</postcode>
  </shipTo>
  <shipTo exportCode="1" xsi:type="ipo:UKAddress">
    <name>Joe Lee</name>
    <street>66 University Avenue</street>
    <city>Palo Alto</city>
    <state>CA</state>
    <postcode>CB1 1JR</postcode>
  </shipTo>
  <billTo xsi:type="ipo:USAddress">
    <name>Robert Smith</name>
    <street>8 Oak Avenue</street>
    <city>Old Town</city>
    <state>PA</state>
    <zip>95819</zip>
  </billTo>
  <items>
    <item partNum="833-AA">
      <productName>Lapis necklace</productName>
      <quantity>1</quantity>
      <USPrice>99.95</USPrice>
      <ipo:comment>Want this for the holidays!</ipo:comment>
      <shipDate>2008-12-05</shipDate>
    </item>
    <item partNum="945-ZG">
      <productName>Sapphire Bracelet</productName>
      <quantity>2</quantity>
      <USPrice>178.99</USPrice>
      <shipDate>2009-01-03</shipDate>
    </item>
  </items>
</ipo:purchaseOrder>

During processing of the order by clause, the expression in the ordering specification is evaluated for each tuple that is generated by the for clause. For the first tuple, the value that is returned by the expression xs:decimal($i/USPrice) is 99.95. The expression is then evaluated for the next tuple, and the expression returns the value 178.99. Because the ordering specification indicates that items are sorted in descending order, the product with the price 99.95 sorts before the product with the price 178.99. This sorting process continues until all tuples are reordered. The return clause then executes once for each tuple in the reordered tuple stream.

The query in the example returns the following result:
Sapphire Bracelet:US$178.99 Lapis necklace:US$99.95

In the example, the expression in the ordering specification constructs an xs:decimal value from the value of the USPrice element. This type conversion is necessary because the type annotation of the USPrice element in the XML schema is xs:untypedAtomic. Without this conversion, the result would use string ordering rather than numeric ordering.

Explicit type conversion is also required when the dynamic type of the ordering key value is xs:untypedAtomic because the rules for comparing ordering keys dictate that untyped atomic data is treated as a string.

Tip: You can use an order by clause in an FLWOR expression to specify value ordering in a query that would otherwise not require iteration. For example, the following path expression returns a list of customerinfo elements with a customer ID (Cid) that is greater than 1000:
$ci/customerinfo[@Cid > "1000"]
To return these items in ascending order by the name of the customer, however, you need to specify an FLWOR expression that includes an order by clause:
SELECT XMLQUERY(
  'declare default element namespace "http://posample.org";
  for $custinfo in $ci/customerinfo
  where ($custinfo/@Cid > 1000)
  order by $custinfo/name ascending
  return $custinfo'
  PASSING XMLAGG(INFO) as "ci")
FROM CUSTOMER
The ordering key does not need to be part of the output. The following query produces a list of product names, in descending order by price, but does not include the price in the output:
SELECT XMLQUERY(
  'declare namespace ipo="http://www.example.com/IPO";
  for $i in $po/ipo:purchaseOrder/items/item
  order by xs:decimal($i/USPrice) descending
  return $i/productName'
  PASSING PORDER as "po")
FROM PURCHASEORDER

Rules for comparing ordering specifications

The process of evaluating and comparing ordering specifications is based on the following rules:
  • The expression in the ordering specification is evaluated and the result is converted to an atomic value. The result of the conversion must be a single atomic value or an empty sequence; otherwise an error is returned. The result of evaluating an ordering specification is called an ordering key.
  • If the type of an ordering key is xs:untypedAtomic, that key is cast to the type xs:string.
  • If the values that are generated by an ordering specification are not all of the same type, those values (keys) are converted to a common type by subtype substitution or type promotion. Keys are compared by converting them to the least common type that supports the gt operator. If the ordering keys that are generated by a given ordering specification do not have a common type that supports the gt operator, an error results.
  • The values of the ordering keys are used to determine the order in which tuples of bound variables are passed to the return clause for execution. The ordering of tuples is determined by comparing their ordering keys, from left to right, by using the following rules:
    • If the sort order is ascending, tuples with ordering keys that are greater than other tuples sort after those tuples.
    • If the sort order is descending, tuples with ordering keys that are greater than other tuples sort before those tuples.
    The greater-than relationship for ordering keys is defined as follows:
    • An empty sequence is greater than all other values.
    • NaN is greater than all other values except the empty sequence.
    • A value is greater than another value if, when the value is compared to another value, the gt operator returns true.
    • Neither of the special floating-point values positive zero or negative zero is greater than the other because +0.0 gt -0.0 and -0.0 gt +0.0 are both false.