Brightlane's export tool is requesting the batch that would cover positions 71 through 80 of the catalogue, using the same page size of 10 and alphabetical sort.
Write a query to return the ID, name, and price for every product in that batch.
Assumptions:
- The
productstable contains exactly 63 products in Brightlane's catalogue. - The batch starts at position 71 —
OFFSET 70, thenLIMIT 10. - Position 71 is past the end of the catalogue (63 < 71), so the result set will contain zero rows. PostgreSQL returns an empty result, not an error.
Output:
- One row per product in the batch, with columns
id,name, andprice, sorted bynameascending. - The result set will be empty.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
SELECT
id,
name,
price
FROM
products
ORDER BY
name
LIMIT
10
OFFSET
70 The shape
The export tool asks for positions 71 through 80, but the catalogue only has 63 products. OFFSET 70 lands past the end of the result set, so the query returns zero rows without raising an error.
Clause by clause
SELECT id, name, pricepicks the three export columns. The shape of theSELECTlist is unchanged from the previous batch query — only the offset moves between requests.FROM productsreads the full catalogue. The query plan still computes the entire sorted result before the window is applied.ORDER BY namesorts alphabetically. The sort still runs even though no row will survive the window, because PostgreSQL can't know the offset is out of bounds until after it has the sorted set in hand.LIMIT 10 OFFSET 70requests up to 10 rows after skipping the first 70. The sorted result has 63 rows; PostgreSQL skips through all of them, finds nothing left, and returns the empty result set. No error, no warning, no padding.
Why this and not a row-count check first
The export tool can ask the question and read the answer instead of asking how big the catalogue is before each request. An empty result is a meaningful answer in itself: "there is no batch at this position." A defensive count query would double the round-trips and still wouldn't help — the count could be stale by the time the second query runs.
This is also why the same query can return rows tomorrow without any code change. As more products are added, the catalogue grows past 70 rows, and the same query starts returning data. The query stays static; the data moves underneath it.
The trap
An empty result set is not an error. A learner who treats zero rows as a failure case will write retry logic or assume the query is wrong, when in fact it has done exactly what it was asked to do — return whatever rows live inside the window. The executable rule: an out-of-bounds OFFSET returns zero rows, the same way an in-bounds OFFSET past the end of the data returns a partial batch. Both are valid responses to a valid query. The signal isn't "something broke," it's "nothing falls inside this window right now."
You practiced requesting an OFFSET that lands past the end of the result set. The recurring lesson: an out-of-bounds OFFSET returns zero rows, never an error — useful to know because the same query may return rows tomorrow as more data arrives.