Analytical Functions: Mastering the OVER() Clause
Move beyond basic GROUP BY. Learn how Oracle Analytical (Window) Functions allow you to calculate running totals, moving averages, and access previous rows without destructive self-joins.
The Limitation of GROUP BY
Standard aggregate functions (SUM, MAX, AVG) coupled with GROUP BY are incredibly useful, but they have a massive limitation: They destroy the detail rows.
If you want to show an employee's salary alongside the average salary of their department, you cannot do it with a simple GROUP BY. You would have to write a subquery to calculate the department average, and then JOIN it back to the main employee table. This is slow and verbose.
Analytical Functions (Window Functions) solve this. They perform aggregates across a set of rows related to the current row, but they do not collapse the result set. You get the aggregate value, and you keep the detail row!
The Anatomy of OVER()
An analytical function is defined by the OVER() clause. It dictates the "Window" of data the function operates on.
The OVER() clause has three main optional components:
PARTITION BY: Divides the result set into groups (likeGROUP BY).ORDER BY: Defines the logical order of rows within the partition.ROWS BETWEEN: Defines a sliding window frame relative to the current row.
Ranking Functions (ROW_NUMBER vs RANK)
These are the most commonly used analytical functions for deduplication and Top-N reporting.
ROW_NUMBER()
Assigns a unique, sequential integer to each row within the partition.
RANK() vs DENSE_RANK()
What if two employees have the exact same salary?
ROW_NUMBER()will arbitrarily assign one as1and the other as2.RANK()will assign both as1, but skip the next number (the next employee gets3).DENSE_RANK()will assign both as1, and NOT skip (the next employee gets2).
Time Travel (LEAD and LAG)
LEAD and LAG allow you to look at data from subsequent or preceding rows without writing expensive, complex Self-Joins. This is vital for calculating Year-Over-Year growth or day-to-day variances.
LAG(column, offset)looks backward.LEAD(column, offset)looks forward.
Defining Windows (ROWS BETWEEN)
When you include an ORDER BY in your OVER() clause, Oracle automatically applies a default window frame: RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. This is what creates a Running Total.
Sliding Windows (Moving Averages)
You can manually override the window frame to calculate things like a 7-day Moving Average.
Common Gotchas
Important Gotchas
- !
Analytical functions evaluate after the
WHEREclause. You cannot put an analytical function inside aWHEREclause (e.g.,WHERE ROW_NUMBER() OVER(...) = 1). You must wrap the analytical query in an Inline View (Subquery or CTE) and filter the result in the outer query. - !
If you use
SUM() OVER(PARTITION BY dept)you get the total for the department on every row. If you add anORDER BYto it:SUM() OVER(PARTITION BY dept ORDER BY date), it suddenly changes from a grand total into a Running Total. Be very careful withORDER BYin aggregates!
Key Takeaways
Key Takeaways
- Analytical Functions allow you to perform grouped calculations without losing the underlying detail rows.
- Use
ROW_NUMBER()combined with a CTE for efficient deduplication and Top-N queries. - Use
LEADandLAGto compare a row against its neighbors without writing Self-Joins. - Master the
ROWS BETWEENsyntax to create powerful moving averages and rolling sums.


