Logical query order is FROM → WHERE → GROUP → HAVING → SELECT/windows → ORDER — the window value doesn't exist yet when WHERE runs, which is why the error is a binder error, not a runtime one. The honest shape is two steps: compute the rank in an inner query, filter rn = 1 outside; QUALIFY is that same two-step with the scaffolding hidden. Top-N inherits the ranking atom's ties lesson too.
SELECT branch, drink FROM sales WHERE ROW_NUMBER() OVER ( PARTITION BY branch ORDER BY units DESC ) = 1 ORDER BY branch;
SELECT branch, drink FROM sales QUALIFY ROW_NUMBER() OVER ( PARTITION BY branch ORDER BY units DESC ) = 1 ORDER BY branch;
Filtering ROW_NUMBER() in WHERE raised a binder error — the window value doesn't exist yet when WHERE runs, so the engine refused rather than guessed; QUALIFY (or a subquery) is the compute-then-filter fix.
Subquery + outer filter (portable), or QUALIFY (DuckDB / Snowflake / BigQuery; Postgres has no QUALIFY — subquery there).
QUALIFY exists in DuckDB, Snowflake, BigQuery; Postgres needs the subquery form.