|
What i like most about window functions is that they give me a way to do a sort of 'extended group by' which i have always wanted. If you want to know the highest salary in each department, that's easy: select department, max(gross_salary)
from salary
group by department
If you want to know who it is who earns that salary, you might try to do this: select department, first_name, max(gross_salary)
from salary
group by department
But this doesn't work, because it's meaningless to ask for first_name in a situation where you're grouping by department. You could ask for an aggregation of all names, but there's no straightforward way to ask for the name of the person who earned that salary. You end up having to write a join against the group by, as in the article, which is pretty grim, and falls apart if you want to order by multiple columns to break ties.Window functions let you re-frame this kind of group by like this: select department, gross_salary
from (
select *, row_number() over (partition by department order by gross_salary desc) as n
from salary
) _
where n = 1
Because the outer query is no longer a group by, you can select any columns you like. The natural query works fine: select department, first_name, gross_salary
from (
select *, row_number() over (partition by department order by gross_salary desc) as n
from salary
) _
where n = 1
This only works where the group by is based on an aggregate function that picks one value, like min or max. I somewhat think it was a mistake to model that kind of thing as an aggregation in the first place. If SQL had a way of picking one row from a group, rather than aggregating over it, that would be immensely useful. |
You can do this with a LATERAL join, if you want to avoid the jankiness of window functions. Lateral joins are just a programmatic way to introduce a correlated subquery. For example
This uses a limit of 3 to show off top-3 instead of just argmax, but you could clearly set that to one if you wanted. This construction can be pretty handy if you need the per-group rows to be something other than what a window function could handle.