QUALIFY
QUALIFY filters rows based on the result of a window function, letting you write conditions like 'keep only the top-ranked row per group' without wrapping the query in a subquery.
Overview
QUALIFY filters the rows produced by window functions (ROW_NUMBER(), RANK(), LAG(), etc.), the same way HAVING filters aggregated groups and WHERE filters raw rows. Without QUALIFY, filtering on a window function result requires wrapping the query in a subquery or CTE just to apply the filter in an outer WHERE clause -- QUALIFY avoids that boilerplate entirely.
Copy code
SELECT schema_name, function_name,
row_number() OVER (PARTITION BY schema_name ORDER BY function_name) AS rn
FROM duckdb_functions()
QUALIFY rn < 3;
Without QUALIFY, the same query needs a subquery:
Copy code
SELECT * FROM (
SELECT schema_name, function_name,
row_number() OVER (PARTITION BY schema_name ORDER BY function_name) AS rn
FROM duckdb_functions()
) t
WHERE rn < 3;
Referencing a window function directly
QUALIFY can reference a window function inline, without it being aliased in the SELECT list:
Copy code
SELECT schema_name, function_name
FROM duckdb_functions()
QUALIFY row_number() OVER (PARTITION BY schema_name ORDER BY function_name) = 1;
Clause position and DuckDB's angle
QUALIFY sits after the (optional) WINDOW clause and before ORDER BY in query execution order -- logically: WHERE -> GROUP BY -> HAVING -> WINDOW -> QUALIFY -> SELECT -> ORDER BY. DuckDB has strong native support for QUALIFY, which pairs naturally with its efficient window function execution, making patterns like "top N rows per group," "deduplicate keeping the latest record," or "find the first event per session" a single flat query instead of a nested one.
Copy code
-- Latest order per customer, no subquery
SELECT customer_id, order_id, order_date
FROM orders
QUALIFY row_number() OVER (PARTITION BY customer_id ORDER BY order_date DESC) = 1;
QUALIFY is also implemented by Snowflake and BigQuery, though it is not part of core ANSI SQL.
Related terms
The OVER clause turns an aggregate or ranking function into a window function by defining the partition, order, and frame it operates on, without collapsing the result set.
ROW_NUMBER →ROW_NUMBER() is a SQL window function that assigns a unique, sequential integer to each row within a partition based on a specified order, with no ties.
window functions →Window functions allow you to perform calculations across a set of table rows that are somehow related to the current row, similar to aggregate functions,…
HAVING clause →The HAVING clause filters grouped results after aggregation, letting you keep or discard groups based on conditions over aggregate values like SUM() or COUNT().
Deduplication →Deduplication is the process of identifying and removing duplicate records from a dataset, keeping only one representative row per logical entity.
Window frame →A window frame is the subset of rows within a window function's partition that a calculation is applied to, defined with ROWS, RANGE, or GROUPS relative to the current row.
FAQS
QUALIFY lets you filter directly on a window function's result in the same SELECT, avoiding the extra subquery or CTE that would otherwise be needed since WHERE cannot reference window functions.
No -- it's not in the ANSI SQL standard, but it's supported by DuckDB, Snowflake, and BigQuery, and DuckDB treats it as a first-class clause alongside WHERE and HAVING.
After WHERE, GROUP BY, HAVING, and window function evaluation, but before ORDER BY -- it filters the rows that window functions have already been computed for.
