Overview
Thelimit-without-order rule flags queries that use LIMIT without an ORDER BY clause. Without explicit ordering, the database may return different rows on each execution, leading to non-deterministic results.
Rule name: limit-without-order
Default severity: Warning
When This Rule Triggers
This rule triggers when:- A
SELECTquery has aLIMITclause but noORDER BYclause - The
LIMITvalue is greater than 1
LIMITis used withORDER BYLIMIT 1is used (commonly for existence checks where order doesn’t matter)- No
LIMITclause is present
Why It Matters
Non-Deterministic Results
WithoutORDER BY, PostgreSQL may return rows in any order, which can vary based on:
- Query execution plan changes
- Index usage changes
- Physical storage order (which changes with
UPDATE,DELETE,VACUUM) - Parallel query execution
- Inconsistent pagination: Different results on each page load
- Flaky tests: Tests that pass/fail randomly
- User confusion: Data appearing and disappearing
- Debugging nightmares: Irreproducible bugs
LIMIT 1 Exception
The rule exemptsLIMIT 1 because it’s commonly used for existence checks or “give me any example” queries where the specific row doesn’t matter:
Examples
Problematic: LIMIT without ORDER BY
Problematic: LIMIT without ORDER BY
Good: LIMIT with ORDER BY
Good: LIMIT with ORDER BY
Allowed: LIMIT 1 for existence check
Allowed: LIMIT 1 for existence check
LIMIT 1, you’re just checking if any row exists. The specific row doesn’t matter.Good: ORDER BY for pagination
Good: ORDER BY for pagination
id to handle ties in created_at.Best Practices
Always Include a Tiebreaker
When ordering by a non-unique column, add a unique column (likeid) as a tiebreaker:
Pagination Pattern
For stable pagination, always useORDER BY with unique constraints:
Implementation Details
The rule works by:- Walking the SQL AST to find
SelectStmtnodes - Checking if
LimitCountis present - Checking if
SortClause(ORDER BY) is empty - Checking if the limit value is 1 (via
isLimitOnehelper) - Reporting a warning at the location of the
LIMITclause
limit_without_order.go