QUALIFY
The QUALIFY clause filters rows on the result of a window function. It is to window functions what HAVING is to GROUP BY: the window is computed first, and QUALIFY then decides which rows survive.
It exists because neither of the other filtering clauses can do this. WHERE runs before the window is computed, so the value is not available to it; HAVING filters a grouped result, and window functions cannot be combined with GROUP BY at all.
Syntax
SELECT <column> [, ...]
FROM <relation_name>
[ WHERE <condition> ]
QUALIFY <window_condition>
[ ORDER BY <column> ]
[ LIMIT <count> ];QUALIFY sits after WHERE and before ORDER BY — matching the order the clauses actually execute in.
Parameters
<window_condition>— a boolean expression that must contain at least one window function. The window function may be a ranking function or an aggregate window, and need not appear in theSELECTlist. Plain columns may be combined into the condition withAND,ORandNOT, but cannot make up the whole of it.
Examples
Top N Rows
SELECT name
FROM $planets
QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 3;
-- Mercury, Venus, EarthThe ordering column does not have to be selected:
SELECT name
FROM $planets
QUALIFY RANK() OVER (ORDER BY mass DESC) <= 3;
-- Jupiter, Saturn, NeptuneFirst Row per Partition
The idiom QUALIFY is most often reached for — one row per group, chosen by an ordering, without a self-join or a subquery:
SELECT name, planetId
FROM testdata.satellites
QUALIFY ROW_NUMBER() OVER (PARTITION BY planetId ORDER BY id) = 1;
-- the first satellite of each planet: Moon, Phobos, Io, Mimas, ...Filtering on an Aggregate Window
Keep only rows whose partition has more than one member:
SELECT name, number_of_moons
FROM $planets
QUALIFY COUNT(*) OVER (PARTITION BY number_of_moons) > 1;
-- Mercury and Venus, the two planets with 0 moonsCombined with WHERE
WHERE filters rows before the window is computed, so the window sees only the surviving rows. The two clauses compose:
SELECT name
FROM $planets
WHERE id > 3
QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 2;
-- Mars, Jupiter — the numbering restarts over the filtered rowsCombining Conditions
A plain column condition can be part of the QUALIFY expression, as long as a window function is in there too:
SELECT name
FROM $planets
QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 5
AND id > 2;
SELECT name
FROM $planets
QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 2
OR ROW_NUMBER() OVER (ORDER BY id) >= 8;Prefer WHERE for the plain part where you can — it filters earlier, and it changes what the window sees.
Over a CTE
WITH x AS (SELECT * FROM $planets)
SELECT name
FROM x
QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 2;Notes
The condition must contain a window function. A
QUALIFYthat does not is rejected:SELECT name FROM $planets QUALIFY id > 5; -- QUALIFY filters on a window function, but this one contains none. Use WHERE to -- filter on plain columns, or HAVING to filter a grouped result.A
SELECTalias is not accepted, even though HAVING accepts one. The window function has to be written out again in theQUALIFYcondition:SELECT name, COUNT(*) OVER (PARTITION BY number_of_moons) AS c FROM $planets QUALIFY c > 1; -- rejected SELECT name, COUNT(*) OVER (PARTITION BY number_of_moons) AS c FROM $planets QUALIFY COUNT(*) OVER (PARTITION BY number_of_moons) > 1; -- worksEvery window function restriction applies here too. The window inside a
QUALIFYis an ordinary window function and carries all of its rules —ORDER BYis required for ranking functions and rejected for aggregate windows,GROUP BYcannot be present anywhere in the query, an aggregate window reads from exactly one relation, andARRAY_AGGandANY_VALUEcannot be used withOVER (). See Window Functions.Use a qualified wildcard, not
SELECT *.SELECT * ... QUALIFY <window>runs, but leaks the window's internal working column (a$win_…name) into the result.SELECT <alias>.*returns only the relation's own columns:SELECT * FROM $planets QUALIFY ROW_NUMBER() OVER (ORDER BY id) <= 2; -- 21 columns: the 20 from $planets, plus a stray `$win_…` SELECT p.* FROM $planets AS p QUALIFY ROW_NUMBER() OVER (ORDER BY p.id) <= 2; -- 20 columns, as expectedQUALIFYruns after the window and beforeORDER BYandLIMIT, so aLIMITapplies to the rowsQUALIFYkept.
See Also
- Window Functions — the functions
QUALIFYfilters on, and their rules - HAVING — the equivalent for
GROUP BY - WHERE — filtering before the window is computed
- SELECT
- ORDER BY