For AI agents: the complete documentation index is at llms.txt. Every page is also available as markdown by appending .md to its URL, or by sending an Accept: text/markdown request header.

Check whether a column is sorted by another column

Sorting one column by another answers questions like whether large trades always print at better prices, or whether a cumulative field ever moves backwards against its key. This recipe reports where that relationship breaks.

Problem​

You want to know whether price only rises as amount grows, without assuming anything about the order the rows are stored in.

Solution​

Order the window by the column you are sorting against, compare each row's value with the previous one, and count the rows that go backwards:

Count rows where price falls as amount growsDemo this query
SELECT count() AS out_of_order_rows
FROM (
SELECT price AS current_price,
lag(price) OVER (ORDER BY amount) AS previous_price
FROM trades
WHERE symbol = 'BTC-USDT' AND timestamp IN '$today'
)
WHERE current_price < previous_price;

ORDER BY amount inside OVER () is what makes this different from checking a column against the designated timestamp. Without it, lag() follows the table's scan order and answers a different question entirely, which is covered in Check timestamp order.

A result of 0 means price never decreases as amount increases. Any other number is the count of positions where it does.

Find the first row that breaks the order​

To locate the break rather than count breaks, number the rows and return the first violation:

Find the first row where price falls as amount growsDemo this query
WITH column_and_prev AS (
SELECT row_number() OVER (ORDER BY amount) AS rownum,
amount,
price AS current_price,
lag(price) OVER (ORDER BY amount) AS previous_price
FROM trades
WHERE symbol = 'BTC-USDT' AND timestamp IN '$today'
)
SELECT rownum, amount, current_price, previous_price
FROM column_and_prev
WHERE current_price < previous_price
ORDER BY rownum
LIMIT 1;

row_number() must use the same ORDER BY as lag(). If it is left as row_number() OVER () it numbers rows in scan order while lag() walks them in amount order, so the reported position belongs to a different sequence than the comparison.

ORDER BY is required before LIMIT

The rows leave the WHERE clause in scan order, not in amount order, so LIMIT 1 on its own returns an arbitrary violation rather than the earliest one. ORDER BY rownum before LIMIT 1 is what makes it the first.

Filtering by a single symbol keeps the comparison meaningful. Across interleaved symbols the query reports breaks that are only an artefact of mixing instruments.