[{"data":1,"prerenderedAt":119},["ShallowReactive",2],{"qa-\u002Fsql\u002Fbasics\u002Fsorting-limiting":3},{"page":4,"siblings":99,"blog":116},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":89,"related":90,"seo":91,"seoDescription":92,"stem":93,"subtopic":94,"topic":95,"topicSlug":96,"updated":97,"__hash__":98},"qa\u002Fsql\u002Fbasics\u002Fsorting-limiting.md","Sorting Limiting",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","SQL","sql",{},true,3,"\u002Fsql\u002Fbasics\u002Fsorting-limiting",[23,27,31,35,40,44,48,52,57,61,65,69,73,77,81,85],{"id":24,"difficulty":14,"q":25,"a":26},"order-by-basics","What does ORDER BY do?","`ORDER BY` **sorts the result set** by one or more columns or expressions. It runs\n**last** in the logical pipeline (just before `LIMIT`), so it can reference `SELECT`\naliases. Without it, row order is **not guaranteed** — the database may return rows\nin any order.\n\n```sql\nSELECT name, created_at\nFROM users\nORDER BY created_at DESC;   -- newest first\n```\n\nSorting happens after filtering and grouping, on the final rows. Large unindexed\nsorts can be expensive because the whole result must be ordered.\n\nRule of thumb: if you care about row order, you must say so with `ORDER BY` — never\nrely on insertion order.\n",{"id":28,"difficulty":14,"q":29,"a":30},"asc-desc","How do you control sort direction?","Append `ASC` (ascending, the default) or `DESC` (descending) to each sort key.\nDirection is **per column**, so you can mix them.\n\n```sql\nSELECT name, age\nFROM users\nORDER BY age DESC, name ASC;   -- oldest first; ties broken alphabetically\n```\n\n`ASC` is implicit, so `ORDER BY age` sorts ascending. Each key after the first only\nbreaks ties left by the preceding keys.\n\nRule of thumb: list sort keys from most significant to least; each one is a\ntie-breaker for the ones before it.\n",{"id":32,"difficulty":14,"q":33,"a":34},"order-by-multiple","How do you sort by multiple columns?","List the columns comma-separated; the database sorts by the **first**, then breaks\nties with the **second**, and so on. Order of the keys matters.\n\n```sql\nSELECT department, salary, name\nFROM employees\nORDER BY department ASC, salary DESC;  -- group by dept, then highest paid first\n```\n\nThis is how you get \"grouped\" looking output without aggregation — rows for the same\ndepartment sit together, ordered within the department.\n\nRule of thumb: put the column you want grouped together first, the tie-breaker\nsecond.\n",{"id":36,"difficulty":37,"q":38,"a":39},"order-by-expression","medium","Can you sort by an expression or alias?","Yes. Because `ORDER BY` runs after `SELECT`, you can sort by a **computed\nexpression**, a **column alias**, or even a **column position number** (`ORDER BY 2`).\n\n```sql\nSELECT name, price * quantity AS total\nFROM order_items\nORDER BY total DESC;        -- alias works here (unlike in WHERE)\n```\n\nSorting by position (`ORDER BY 2`) is terse but fragile — reordering the `SELECT`\nlist silently changes the sort. Prefer aliases for clarity.\n\nRule of thumb: sort by alias for readability; avoid positional `ORDER BY` numbers\nin production code.\n",{"id":41,"difficulty":37,"q":42,"a":43},"null-ordering","Where do NULLs sort in ORDER BY?","It's **dialect-dependent**. Postgres\u002FOracle sort `NULL`s **last** in `ASC` (first in\n`DESC`); MySQL\u002FSQL Server sort them **first** in `ASC`. The SQL standard lets you\ncontrol it with `NULLS FIRST` \u002F `NULLS LAST`.\n\n```sql\n-- Postgres\u002FOracle: force NULLs to the bottom regardless of direction\nSELECT name, last_login\nFROM users\nORDER BY last_login DESC NULLS LAST;\n```\n\nMySQL lacks `NULLS LAST`, so you emulate it: `ORDER BY last_login IS NULL,\nlast_login DESC`.\n\nRule of thumb: if NULLs matter in your sort, state `NULLS FIRST\u002FLAST` explicitly\nrather than trusting the default.\n",{"id":45,"difficulty":14,"q":46,"a":47},"limit-clause","How do you limit the number of rows returned?","Use `LIMIT n` (Postgres\u002FMySQL\u002FSQLite), `FETCH FIRST n ROWS ONLY` (SQL standard \u002F\nOracle \u002F DB2), or `SELECT TOP n` (SQL Server). It caps the result to the first `n`\nrows **after** ordering.\n\n```sql\nSELECT name, score\nFROM players\nORDER BY score DESC\nLIMIT 10;                  -- top 10 scorers\n```\n\n`LIMIT` is applied last, so it works on the sorted result. A `LIMIT` with no\n`ORDER BY` returns an arbitrary subset.\n\nRule of thumb: always pair `LIMIT` with `ORDER BY` for a deterministic \"top N\".\n",{"id":49,"difficulty":37,"q":50,"a":51},"offset-pagination","How do you paginate results with OFFSET?","`LIMIT n OFFSET m` skips the first `m` rows and returns the next `n` — the classic\npage query. Page `p` (1-based, page size `s`) uses `OFFSET (p - 1) * s`.\n\n```sql\n-- page 3, 20 rows per page  ->  skip 40, take 20\nSELECT id, title\nFROM articles\nORDER BY published_at DESC, id DESC\nLIMIT 20 OFFSET 40;\n```\n\nAlways order by something **unique** (add `id` as a tie-breaker) so rows don't shift\nbetween pages. `OFFSET` still **scans and discards** the skipped rows, so deep pages\nget slow.\n\nRule of thumb: `OFFSET` is fine for early pages; for deep pagination use keyset\npagination.\n",{"id":53,"difficulty":54,"q":55,"a":56},"keyset-pagination","hard","What is keyset (cursor) pagination and why is it faster?","Keyset pagination fetches the next page by **filtering past the last row seen**\ninstead of counting an offset. The database jumps straight to the position via an\nindex, so cost stays constant no matter how deep you page.\n\n```sql\n-- next page after the last (published_at, id) you saw\nSELECT id, title, published_at\nFROM articles\nWHERE (published_at, id) \u003C ('2026-06-01 10:00', 5000)\nORDER BY published_at DESC, id DESC\nLIMIT 20;\n```\n\nUnlike `OFFSET`, it doesn't scan-and-throw-away earlier rows, and it's stable when\nrows are inserted. The trade-off: you can't jump to an arbitrary page number.\n\nRule of thumb: use keyset pagination for infinite scroll \u002F deep pages; `OFFSET`\nonly for shallow, numbered pages.\n",{"id":58,"difficulty":54,"q":59,"a":60},"offset-performance","Why does OFFSET get slow for deep pages?","`OFFSET m` doesn't skip rows for free — the database must **generate and discard**\nall `m` preceding rows to reach the page. At `OFFSET 1000000`, it produces a million\nrows just to throw them away.\n\n```sql\n-- reads ~100020 rows, returns 20 — the 100000 are wasted work\nSELECT * FROM events ORDER BY created_at LIMIT 20 OFFSET 100000;\n```\n\nCost grows linearly with the offset. Keyset pagination avoids this by seeking\ndirectly to the boundary with an indexed `WHERE`.\n\nRule of thumb: if page numbers reach the thousands, switch from `OFFSET` to keyset\npagination.\n",{"id":62,"difficulty":54,"q":63,"a":64},"top-n-per-group-offset","How do you get the top N rows per group?","A plain `LIMIT` caps the **whole** result, not per group. To get the top N **within\neach group**, rank rows inside each partition with a window function and filter.\n\n```sql\n-- top 3 highest-paid employees per department\nSELECT *\nFROM (\n  SELECT name, department, salary,\n         ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn\n  FROM employees\n) ranked\nWHERE rn \u003C= 3;\n```\n\nUse `ROW_NUMBER` for an exact N, `RANK`\u002F`DENSE_RANK` to include ties. Postgres also\noffers `LATERAL` joins for the same job.\n\nRule of thumb: \"top N per group\" means a window function, not `LIMIT`.\n",{"id":66,"difficulty":37,"q":67,"a":68},"order-by-case","How do you sort by a custom order?","Use a `CASE` expression (or a lookup) in `ORDER BY` to map values to a sort rank —\nhandy for non-alphabetical orderings like priority levels.\n\n```sql\nSELECT title, priority\nFROM tickets\nORDER BY CASE priority\n           WHEN 'high'   THEN 1\n           WHEN 'medium' THEN 2\n           WHEN 'low'    THEN 3\n         END;\n```\n\nWithout this, `ORDER BY priority` would sort alphabetically (`high`, `low`,\n`medium`) — rarely what you want. MySQL also has `FIELD()` as a shortcut.\n\nRule of thumb: encode custom sort orders with a `CASE` rank in `ORDER BY`.\n",{"id":70,"difficulty":37,"q":71,"a":72},"stable-sort","Why should you add a unique tie-breaker to ORDER BY?","When the sort keys aren't unique, rows with equal keys can come back in **any\norder**, and that order may differ between runs or pages. Adding a unique\ntie-breaker (like the primary key) makes the sort **deterministic**.\n\n```sql\n-- created_at ties resolved consistently by id\nSELECT * FROM orders\nORDER BY created_at DESC, id DESC\nLIMIT 20;\n```\n\nThis matters most for pagination: without a stable order, the same row can appear on\ntwo pages or be skipped.\n\nRule of thumb: end every paginated `ORDER BY` with a unique column.\n",{"id":74,"difficulty":54,"q":75,"a":76},"limit-with-ties","How do you include rows tied with the Nth row?","A plain `LIMIT` cuts off at exactly N rows, even if the (N+1)th ties the Nth. The\nstandard `FETCH FIRST n ROWS WITH TIES` (Postgres 13+, SQL Server, Oracle) keeps all\ntied rows.\n\n```sql\n-- the top 3 scores, plus anyone tied with 3rd place\nSELECT name, score\nFROM players\nORDER BY score DESC\nFETCH FIRST 3 ROWS WITH TIES;\n```\n\nIt requires an `ORDER BY` (ties are defined by it). Without `WITH TIES`, you'd\narbitrarily drop one of the tied players.\n\nRule of thumb: use `WITH TIES` when \"top N\" should never split a tie.\n",{"id":78,"difficulty":54,"q":79,"a":80},"distinct-on","How do you get one row per group (Postgres DISTINCT ON)?","Postgres's `DISTINCT ON (cols)` keeps the **first row per group** as defined by\n`ORDER BY`. It's a concise way to grab the latest\u002Flargest row per key without a\nwindow function.\n\n```sql\n-- the most recent order per user\nSELECT DISTINCT ON (user_id) user_id, id, created_at\nFROM orders\nORDER BY user_id, created_at DESC;\n```\n\nThe leading `ORDER BY` columns **must** start with the `DISTINCT ON` columns. Other\ndatabases achieve this with `ROW_NUMBER() = 1`.\n\nRule of thumb: `DISTINCT ON` is Postgres shorthand for \"latest row per group.\"\n",{"id":82,"difficulty":54,"q":83,"a":84},"collation","What is collation and how does it affect sorting?","A **collation** defines the rules for comparing and ordering text — case sensitivity,\naccent handling, and locale-specific alphabet order. Two databases with different\ncollations can sort the same strings differently.\n\n```sql\n-- force a specific collation for this sort (Postgres)\nSELECT name FROM users ORDER BY name COLLATE \"de-DE-x-icu\";\n```\n\nCollation affects `ORDER BY`, `=`\u002F`LIKE` comparisons, and unique constraints. A\nmismatch between two columns being compared can even cause errors or prevent index\nuse.\n\nRule of thumb: set collation deliberately when sorting human-readable text across\nlocales.\n",{"id":86,"difficulty":37,"q":87,"a":88},"random-order","How do you return rows in random order?","Order by a random function: `ORDER BY RANDOM()` (Postgres\u002FSQLite) or `ORDER BY\nRAND()` (MySQL). Combined with `LIMIT`, it samples random rows.\n\n```sql\nSELECT * FROM questions\nORDER BY RANDOM()\nLIMIT 5;                 -- 5 random questions\n```\n\nThis sorts the **entire table** by a random value first, so it's slow on large\ntables. For big tables, sample with a `WHERE random() \u003C 0.01` pre-filter or use\n`TABLESAMPLE`.\n\nRule of thumb: `ORDER BY RANDOM()` is fine for small tables; sample first on large\nones.\n",16,null,{"description":11},"SQL ORDER BY and LIMIT interview questions — sorting direction, NULL ordering, pagination with OFFSET, keyset pagination and dialect differences.","sql\u002Fbasics\u002Fsorting-limiting","Sorting & Limiting","Query Basics","basics","2026-06-20","IlsiDok4lcx1ZfIwq03O5MLmxe-f0biY8gYoImgk4wI",[100,104,107,108,112],{"subtopic":101,"path":102,"order":103},"Joins","\u002Fsql\u002Fbasics\u002Fjoins",1,{"subtopic":105,"path":106,"order":12},"SELECT & WHERE","\u002Fsql\u002Fbasics\u002Fselect-where",{"subtopic":94,"path":21,"order":20},{"subtopic":109,"path":110,"order":111},"Aggregation & GROUP BY","\u002Fsql\u002Fbasics\u002Faggregation",4,{"subtopic":113,"path":114,"order":115},"Set Operations","\u002Fsql\u002Fbasics\u002Fset-operations",5,{"path":117,"title":118},"\u002Fblog\u002Fsql-sorting-limiting-order-by","SQL ORDER BY, LIMIT & OFFSET — Sorting and Paging Results",1782244106844]