✏️ Explanatory Question
Level: Hard — The "detail + summary together" problem; window aggregates solve it elegantly where GROUP BY can't.
GROUP BY, you lose the individual rows. With a subquery, you scan the table twice. What's the clean solution?
employees| name | dept_id | salary |
|---|---|---|
| Rumman | 10 | 90000 |
| Krushna | 10 | 60000 |
| Swetha | 10 | 50000 |
| Ritesh | 20 | 80000 |
| Manjula | 20 | 40000 |
The key insight: An aggregate window function (like SUM() OVER (PARTITION BY ...)) computes a group total without collapsing the rows. Unlike GROUP BY, which returns one row per group, the window version keeps every detail row and attaches the group's aggregate to each.
GROUP BY SUM() → one row per department (detail lost). SUM() OVER (PARTITION BY dept_id) → every employee row kept, each showing its department's total. Same aggregate, different granularity of output.
SELECT
name,
dept_id,
salary,
SUM(salary) OVER (PARTITION BY dept_id) AS dept_total,
ROUND(
salary / SUM(salary) OVER (PARTITION BY dept_id) * 100, 1
) AS pct_of_dept,
AVG(salary) OVER (PARTITION BY dept_id) AS dept_avg,
COUNT(*) OVER (PARTITION BY dept_id) AS dept_headcount
FROM employees
ORDER BY dept_id, salary DESC;
| name | dept_id | salary | dept_total | pct_of_dept | dept_avg | headcount |
|---|---|---|---|---|---|---|
| Rumman | 10 | 90000 | 200000 | 45.0 | 66667 | 3 |
| Krushna | 10 | 60000 | 200000 | 30.0 | 66667 | 3 |
| Swetha | 10 | 50000 | 200000 | 25.0 | 66667 | 3 |
| Ritesh | 20 | 80000 | 120000 | 66.7 | 60000 | 2 |
| Manjula | 20 | 40000 | 120000 | 33.3 | 60000 | 2 |
Every employee row is preserved, each carrying its department's totals and the individual's share.
SELECT
name, salary,
SUM(salary) OVER () AS grand_total, -- whole table
ROUND(salary / SUM(salary) OVER () * 100, 1) AS pct_of_all
FROM employees;
-- An empty OVER () treats the ENTIRE result set as one window
| Approach | Keeps detail rows? | Table scans |
|---|---|---|
| Window aggregate | Yes | One pass |
| GROUP BY | No (collapses) | One pass |
| Correlated subquery | Yes | Repeated (slow) |
ORDER BY inside the window turns a plain aggregate into a running total (with the default frame). For a flat group total, use PARTITION BY without an ORDER BY inside the OVER clause.
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) and SUM(salary) OVER (PARTITION BY dept_id) — you can use multiple window functions with different (or shared) OVER clauses in one SELECT.