7.10.10. SQL-10 — _order_by and _order_by_descending

_order_by(key) adds an ORDER BY clause. The key shape decides what the macro emits:

Key shape

Emitted SQL

_.Field

ORDER BY "Field" ASC

(_.k1, _.k2, ...)

ORDER BY "k1" ASC, "k2" ASC, ...

_.expr (e.g. _.Price / 10)

ORDER BY (expr) ASC (constants inlined)

_order_by_descending(_.Field)

ORDER BY "Field" DESC

_order_by_keys((_.k1, _.k2), mask)

ORDER BY "k1" ASC, "k2" DESC (per bit)

7.10.10.1. Single-column order

let by_price <- _sql(db |> select_from(type<Car>)
                       |> _order_by(_.Price))
// SELECT "Id", "Name", "Price" FROM "Cars" ORDER BY "Price" ASC

_order_by_descending flips the direction:

let by_price_desc <- _sql(db |> select_from(type<Car>)
                            |> _order_by_descending(_.Price))
// ... ORDER BY "Price" DESC

7.10.10.2. Multi-column tuple key

A tuple-shaped key emits each tuple field as its own ORDER BY column. Useful for primary-and-tie-breaker ordering:

let by_price_then_name <- _sql(db |> select_from(type<Car>)
                                 |> _order_by((_.Price, _.Name)))
// ... ORDER BY "Price" ASC, "Name" ASC

If two rows tie on Price, the second column breaks the tie.

7.10.10.3. Computed key

The key can be a computed expression, not just a column reference. Constants are inlined (the key fragment carries no bind), mirroring computed _group_by keys:

let by_decile <- _sql(db |> select_from(type<Car>)
                        |> _order_by(_.Price / 10))
// ... ORDER BY ("Price") / (10) ASC

A tuple key may mix computed entries and plain columns (_order_by((_.Price / 100, _.Name))ORDER BY ("Price") / (100) ASC, "Name" ASC). A key referencing a runtime value (not a constant) is rejected — inline a literal or drop to raw SQL.

A string-returning expression over the row is a computed key too — _order_by(to_lower(_.Name))ORDER BY LOWER("Name") for a case-insensitive sort. (A plain string variable with no column reference is instead treated as a dynamic column name.)

7.10.10.4. Mixed ASC/DESC

_order_by and _order_by_descending emit every column in the same direction. For per-column direction, use _order_by_keys: it takes the key tuple plus a uint mask, where bit i sets key i to DESC (LSB = first key, a clear bit stays ASC):

let mixed <- _sql(db |> select_from(type<Car>)
                    |> _order_by_keys((_.Price, _.Name), 2u))
// ... ORDER BY "Price" ASC, "Name" DESC

Mask 2u is 0b10 — bit 0 clear (Price ASC), bit 1 set (Name DESC). 1u flips the first key, 3u flips both, 0u is all-ascending (same as a plain tuple _order_by). Like the other ordering steps, ordering may appear at most once in a chain.

7.10.10.5. Composes with _where and take

_order_by is just another chain stage — it composes with filter, project, paginate, and aggregate operators in the natural SQL order:

let cheap_top3 <- _sql(db |> select_from(type<Car>)
                         |> _where(_.Price > 50)
                         |> _order_by(_.Price)
                         |> take(3))
// ... WHERE "Price" > ? ORDER BY "Price" ASC LIMIT ?

Note the SQL clause order: WHERE first, ORDER BY next, LIMIT last — same as the chain order.

7.10.10.6. Stable pagination

LIMIT/OFFSET without ORDER BY is non-deterministic — SQLite is free to return rows in any order. Pair take/skip with _order_by whenever you actually care about page boundaries. See SQL-11 — take and skip: Paging.