Skip to main content
ANVISoftware Solutions
Lesson 7 of 22Beginner13 min

Sorting and Limiting

By the end of this lesson

Order results and return only the rows you need.

Without ORDER BY, a database makes no promise about row order. It returns rows in whatever order was cheapest, which often looks sorted because that is how they happen to be stored. Add an index, change the amount of data, or run the query on a bigger server, and the order can change with no warning and no error.

So the rule is simple to state: if the order matters, say so. Never rely on the order you observed.

Sorting on one column and several
SQL
-- Ascending is the default
SELECT product_name, unit_price
FROM products
ORDER BY unit_price;

-- Most expensive first
SELECT product_name, unit_price
FROM products
ORDER BY unit_price DESC;

-- Group by category alphabetically, then price high to low within each
SELECT category, product_name, unit_price
FROM products
ORDER BY category ASC, unit_price DESC;
  • ASC means ascending and is the default, so it is usually left out. DESC means descending and must be written.
  • With several sort columns, the second is only consulted where the first ties. DESC applies to the one column it follows, not to the whole list.
  • ORDER BY is resolved after SELECT, so you can sort by an alias you defined in the SELECT list.

Where NULLs sort, and why it varies

NULL is not greater or smaller than a real value, so each database picks a convention. SQL Server and MySQL sort NULLs first when ascending. PostgreSQL and Oracle sort them last.

If it matters, be explicit. PostgreSQL and Oracle accept ORDER BY credit_limit DESC NULLS LAST. SQL Server does not support that clause, so you sort on a flag first.

Forcing NULLs last in SQL Server
SQL
SELECT c.company_name, c.credit_limit
FROM customers AS c
ORDER BY
    CASE WHEN c.credit_limit IS NULL THEN 1 ELSE 0 END,
    c.credit_limit DESC;
  • The CASE expression produces 0 for rows with a value and 1 for rows without, so sorting on it puts the NULLs at the end.
  • The real sort then runs as a tiebreaker within each group. CASE is SQL's if-then-else expression and appears throughout the rest of this course.

Returning only the rows you need

Row limiting is the one area where dialects differ most. Both forms below are common, and you will meet both:

 SQL ServerPostgreSQL / MySQL / SQLite
First 10 rowsSELECT TOP 10 ... ORDER BY ...SELECT ... ORDER BY ... LIMIT 10
Rows 21 to 30ORDER BY ... OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLYORDER BY ... LIMIT 10 OFFSET 20
Standard SQL formOFFSET / FETCH, supported from SQL Server 2012PostgreSQL supports OFFSET / FETCH too; MySQL and SQLite use LIMIT only
Requires ORDER BYNot enforced, but meaningless without itNot enforced, but meaningless without it
The ten highest-value products, and page three of a list
SQL
-- Top 10, SQL Server
SELECT TOP 10 p.product_name, p.unit_price
FROM products AS p
WHERE p.discontinued = 0
ORDER BY p.unit_price DESC;

-- Page 3 of 20-row pages, standard form
SELECT o.order_id, o.order_date, o.shipping_fee
FROM orders AS o
ORDER BY o.order_date DESC, o.order_id DESC
OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY;
  • OFFSET 40 skips 40 rows, so page 3 of 20-row pages starts at row 41. The offset is (page number - 1) multiplied by page size.
  • The second sort column, order_id DESC, is a tiebreaker. Without it, two orders on the same date have no defined relative order, so a row can appear on both page 2 and page 3 while another never appears at all.
  • That tiebreaker needs to be unique. order_id is the primary key, which makes the whole sort deterministic.

Summary

  • Without ORDER BY there is no guaranteed row order, however sorted the output looks
  • Multiple sort columns are consulted in turn, and DESC applies only to the column it follows
  • NULL sort position differs by database, so be explicit when it matters
  • Paging needs a unique tiebreaker, and deep OFFSET values get slower because skipped rows are still produced

Practice

Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.

Try it yourself

Try it yourself

Return the five most recently hired employees, showing full name, job title and hire date. Two people were hired on the same day, so make sure the result is stable between runs.

Show solution

hire_date DESC gets the recent ones first. The tiebreaker is the part that matters: with two people sharing a hire date, the database is free to order them either way, so which one appears fifth could change between runs.

employee_id DESC as a second sort column makes the result deterministic, because the primary key is unique. Any unique column would do.

SQL
SELECT TOP 5
    e.first_name + ' ' + e.last_name AS employee,
    e.job_title,
    e.hire_date
FROM employees AS e
ORDER BY e.hire_date DESC, e.employee_id DESC;

Knowledge check

Nothing is recorded and there is no score. The explanation appears either way.

Why should a paged query always include a unique column in its ORDER BY?

Saved in this browser only.