Window Functions in SQL - Interview Questions and Answers
Window functions perform calculations across a set of table rows related to the current row, without collapsing rows like GROUP BY
.
Example:
SELECT EmployeeID, Name, Salary,
AVG(Salary) OVER() AS AvgSalary
FROM Employees;
Calculates the average salary without grouping rows.
Feature | Window Functions | Aggregate Functions |
---|---|---|
Row Count | Retains all rows | Groups rows into one |
Syntax | Uses OVER() | Uses GROUP BY |
Example | AVG(Salary) OVER() | AVG(Salary) GROUP BY Department |
ROW_NUMBER()
assigns a unique row number to each record within a partition.
Example: Rank employees by salary
SELECT EmployeeID, Name, Salary,
ROW_NUMBER() OVER(ORDER BY Salary DESC) AS RowNum
FROM Employees;
RANK()
assigns ranking numbers but skips numbers for duplicate values.
Example:
SELECT EmployeeID, Name, Salary,
RANK() OVER(ORDER BY Salary DESC) AS Rank
FROM Employees;
If two employees have the same salary, they get the same rank, but the next rank is skipped.
Function | Behavior | Example |
---|---|---|
RANK() | Skips numbers for duplicates | 1, 2, 2, 4 |
DENSE_RANK() | No gaps in ranking | 1, 2, 2, 3 |
Example:
SELECT EmployeeID, Name, Salary,
DENSE_RANK() OVER(ORDER BY Salary DESC) AS DenseRank
FROM Employees;
NTILE(n)
distributes rows into n equal groups.
Example: Divide employees into 4 salary groups
SELECT EmployeeID, Name, Salary,
NTILE(4) OVER(ORDER BY Salary DESC) AS SalaryGroup
FROM Employees;
It calculates a running total within a window.
Example: Running total of employee salaries
SELECT EmployeeID, Name, Salary,
SUM(Salary) OVER(ORDER BY EmployeeID) AS RunningTotal
FROM Employees;
Function | Behavior |
---|---|
SUM(Salary) | Aggregates total salary |
SUM(Salary) OVER() | Running total without collapsing rows |
AVG()
computes the average over a window.
Example:
SELECT EmployeeID, Name, Salary,
AVG(Salary) OVER(PARTITION BY Department) AS DeptAvgSalary
FROM Employees;
Calculates average salary per department.
Returns the first value in the window.
Example:
SELECT EmployeeID, Name, Salary,
FIRST_VALUE(Salary) OVER(ORDER BY Salary DESC) AS HighestSalary
FROM Employees;
Returns the last value in the window.
Example:
SELECT EmployeeID, Name, Salary,
LAST_VALUE(Salary) OVER(ORDER BY Salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS LowestSalary
FROM Employees;
LAG()
fetches the previous row?s value.
Example: Compare employee salary with previous row
SELECT EmployeeID, Name, Salary,
LAG(Salary, 1) OVER(ORDER BY EmployeeID) AS PrevSalary
FROM Employees;
LEAD()
fetches the next row?s value.
Example: Compare salary with next row
SELECT EmployeeID, Name, Salary,
LEAD(Salary, 1) OVER(ORDER BY EmployeeID) AS NextSalary
FROM Employees;
It groups data like GROUP BY
, but retains all rows.
Example: Calculate department-wise salary rank
SELECT EmployeeID, Name, Department, Salary,
RANK() OVER(PARTITION BY Department ORDER BY Salary DESC) AS DeptRank
FROM Employees;
It defines the order of calculations inside a window.
Example: Running total in order of hiring date
SELECT EmployeeID, Name, Salary, HireDate,
SUM(Salary) OVER(ORDER BY HireDate) AS RunningTotal
FROM Employees;
Clause | Behavior |
---|---|
ROWS | Uses physical row count |
RANGE | Uses logical grouping |
Example: Running total for last 2 rows
SUM(Salary) OVER(ORDER BY EmployeeID ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
Yes!
Example:
SELECT EmployeeID, Name, Salary,
RANK() OVER(ORDER BY Salary DESC) AS Rank,
SUM(Salary) OVER(ORDER BY EmployeeID) AS RunningTotal
FROM Employees;
A window frame defines the subset of rows within a partition used for calculations.
Example: Running total using a frame
SELECT EmployeeID, Name, Salary,
SUM(Salary) OVER(ORDER BY EmployeeID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningTotal
FROM Employees;
Clause | Meaning |
---|---|
UNBOUNDED PRECEDING | Includes all previous rows |
UNBOUNDED FOLLOWING | Includes all future rows |
Example: Total salary for all employees
SUM(Salary) OVER(ORDER BY EmployeeID ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
It refers to the row being processed.
Example: Running average of last 3 rows
AVG(Salary) OVER(ORDER BY EmployeeID ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
Clause | Behavior |
---|---|
ROWS | Uses physical row count |
RANGE | Uses value-based grouping |
Example:
SUM(Salary) OVER(ORDER BY EmployeeID RANGE BETWEEN INTERVAL 100 PRECEDING AND CURRENT ROW)
Includes rows within Salary range of 100, not just the last 3 rows.
Example: Sales of different products in an e-commerce company
SELECT OrderID, CustomerName, Product, OrderAmount,
SUM(OrderAmount) OVER(ORDER BY OrderDate) AS RunningTotal
FROM Orders;
SELECT StudentID, Name, Marks,
RANK() OVER(ORDER BY Marks DESC) AS Rank
FROM Students;
Handles students with the same marks correctly.
SELECT CustomerID, TransactionID, Amount,
LAG(Amount, 1) OVER(PARTITION BY CustomerID ORDER BY TransactionDate) AS PreviousTransaction,
LEAD(Amount, 1) OVER(PARTITION BY CustomerID ORDER BY TransactionDate) AS NextTransaction
FROM Transactions;
SELECT EmployeeID, Name, Department, Salary,
FIRST_VALUE(Salary) OVER(PARTITION BY Department ORDER BY Salary) AS FirstSalary,
LAST_VALUE(Salary) OVER(PARTITION BY Department ORDER BY Salary ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS LastSalary
FROM Employees;
SELECT SalespersonID, Name, Region, SalesAmount,
RANK() OVER(PARTITION BY Region ORDER BY SalesAmount DESC) AS RegionRank
FROM Sales;
SELECT EmployeeID, Name, Department, Salary
FROM (
SELECT EmployeeID, Name, Department, Salary,
RANK() OVER(PARTITION BY Department ORDER BY Salary DESC) AS Rank
FROM Employees
) Ranked
WHERE Rank = 1;
SELECT OrderID, CustomerID, OrderDate, Amount,
AVG(Amount) OVER(ORDER BY OrderDate ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAvg
FROM Orders;
SELECT OrderID, CustomerID, OrderAmount,
SUM(OrderAmount) OVER(ORDER BY OrderID) * 100 / SUM(OrderAmount) OVER() AS CumulativePercentage
FROM Orders;
SELECT Month, TotalSales,
(TotalSales - LAG(TotalSales, 1) OVER(ORDER BY Month)) * 100 / LAG(TotalSales, 1) OVER(ORDER BY Month) AS GrowthPercentage
FROM MonthlySales;
Window functions don?t directly use indexes but benefit from proper partitioning and sorting.
- Use PARTITION BY only when needed
- Reduce ORDER BY complexity
- Avoid using very large window frames
No, DISTINCT
cannot be used inside OVER()
.
- Check execution plan (
EXPLAIN ANALYZE
) - Use indexes on ORDER BY columns
- Optimize window frame definitions
Most modern databases:
- MySQL 8+
- PostgreSQL
- SQL Server
- Oracle 12c+
No, Window Functions cannot be used in WHERE
or HAVING
, only in SELECT
and ORDER BY
.
Use a subquery or Common Table Expression (CTE).
Example:
WITH RankedEmployees AS (
SELECT EmployeeID, Name, Salary,
RANK() OVER(ORDER BY Salary DESC) AS Rank
FROM Employees
)
SELECT * FROM RankedEmployees WHERE Rank <= 5;
Yes, but only in databases that allow it, like PostgreSQL.
Example:
UPDATE Employees
SET Bonus = Salary * 0.10
WHERE EmployeeID IN (
SELECT EmployeeID FROM (
SELECT EmployeeID, RANK() OVER(ORDER BY Salary DESC) AS Rank
FROM Employees
) Ranked WHERE Rank <= 5
);
SELECT EmployeeID, Name, Salary,
PERCENT_RANK() OVER(ORDER BY Salary) AS SalaryPercentile
FROM Employees;
Without ORDER BY
, functions like RANK()
assign random results instead of meaningful order.
SELECT Month, Revenue,
AVG(Revenue) OVER(ORDER BY Month ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS MovingAvgRevenue
FROM MonthlyRevenue;
Helps in financial forecasting by calculating a rolling 6-month revenue average.
CUME_DIST()
calculates the cumulative distribution percentile for a row.
Example: Find the percentile rank of employees by salary
SELECT EmployeeID, Name, Salary,
CUME_DIST() OVER(ORDER BY Salary DESC) AS SalaryPercentile
FROM Employees;
Returns values between 0 and 1, showing the proportion of employees earning less than or equal to the given salary.
Function | Meaning | Formula |
---|---|---|
PERCENT_RANK() | Rank percentage (excluding self) | (Rank - 1) / (TotalRows - 1) |
CUME_DIST() | Cumulative percentile (including self) | Rank / TotalRows |
Example:
SELECT EmployeeID, Name, Salary,
PERCENT_RANK() OVER(ORDER BY Salary) AS PctRank,
CUME_DIST() OVER(ORDER BY Salary) AS CumeDist
FROM Employees;
NTILE(n)
divides rows into n equal-sized buckets.
Example: Divide employees into 4 salary brackets
SELECT EmployeeID, Name, Salary,
NTILE(4) OVER(ORDER BY Salary DESC) AS SalaryBracket
FROM Employees;
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Name, Age, Address ORDER BY ID) AS RowNum
FROM Customers;
Returns duplicate rows with RowNum > 1.
SELECT * FROM (
SELECT ProductID, ProductName, Category, Sales,
RANK() OVER(PARTITION BY Category ORDER BY Sales DESC) AS Rank
FROM Products
) Ranked WHERE Rank <= 3;
Extracts the top-selling products within each category.
SELECT Quarter, Region, Sales,
SUM(Sales) OVER(PARTITION BY Region ORDER BY Quarter) AS CumulativeSales
FROM SalesData;
SELECT EmployeeID, Name,
CASE WHEN MOD(ROW_NUMBER() OVER(ORDER BY Salary), 2) = 0 THEN 'Even' ELSE 'Odd' END AS RankType
FROM Employees;
SELECT OrderDate, SalesAmount,
SUM(SalesAmount) OVER(ORDER BY OrderDate ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS WeeklySales
FROM Sales;
SELECT EmployeeID, Name, Salary, SalaryDate,
LAG(Salary, 1) OVER(PARTITION BY EmployeeID ORDER BY SalaryDate) AS PreviousSalary,
Salary - LAG(Salary, 1) OVER(PARTITION BY EmployeeID ORDER BY SalaryDate) AS SalaryChange
FROM EmployeeSalaries;
Without ORDER BY
, ranking and offset functions may return unpredictable results.
No, Window Functions cannot be used in HAVING
or WHERE
, only in SELECT
or ORDER BY
.
SELECT * FROM (
SELECT EmployeeID, Name, Department, PerformanceScore,
RANK() OVER(PARTITION BY Department ORDER BY PerformanceScore DESC) AS Rank
FROM Employees
) Ranked WHERE Rank <= 5;
Instead of repeating OVER()
in multiple functions, use WINDOW:
SELECT EmployeeID, Name, Salary,
SUM(Salary) OVER emp_window AS RunningTotal,
AVG(Salary) OVER emp_window AS AvgSalary
FROM Employees
WINDOW emp_window AS (PARTITION BY Department ORDER BY Salary);
SELECT * FROM (
SELECT CustomerID, TransactionID, Amount,
ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY TransactionDate) AS RowNum
FROM Transactions
) Ranked WHERE RowNum <= 10;
- Ranking functions treat NULLs as lowest values.
- Offset functions return NULL if no previous/next value exists.
Example:
SELECT EmployeeID, Name, Salary,
LAG(Salary, 1, 0) OVER(ORDER BY Salary) AS PrevSalary
FROM Employees;
Replaces NULL with 0.
No, DISTINCT
is not allowed inside OVER()
.
Use COALESCE()
with LAG()
:
SELECT Month,
COALESCE(Sales, LAG(Sales) OVER(ORDER BY Month)) AS FilledSales
FROM SalesData;
Fills missing sales data with the previous month's value.
SELECT EmployeeID, Name, Salary,
CASE
WHEN PERCENT_RANK() OVER(ORDER BY Salary) <= 0.25 THEN Salary * 1.10
ELSE Salary * 1.05
END AS NewSalary
FROM Employees;
Employees in the lowest 25% salary get a 10% raise, others get 5%.
SELECT OrderID, OrderDate,
NTILE(5) OVER(ORDER BY OrderDate) AS BatchNumber
FROM Orders;
Divides orders into 5 processing batches.
Random Blogs
- Variable Assignment in Python
- Google’s Core Update in May 2020: What You Need to Know
- Top 10 Knowledge for Machine Learning & Data Science Students
- The Ultimate Guide to Artificial Intelligence (AI) for Beginners
- Datasets for analyze in Tableau
- 10 Awesome Data Science Blogs To Check Out
- Understanding OLTP vs OLAP Databases: How SQL Handles Query Optimization
- Convert RBG Image to Gray Scale Image Using CV2
- Data Analytics: The Power of Data-Driven Decision Making
- Downlaod Youtube Video in Any Format Using Python Pytube Library