Department Budgets vs. Average
Write a PostgreSQL query to calculate the total salary budget for each department. Then, determine the average budget across all departments and identify which departments have a total budget strictly greater than this average. Display the department name, its total budget, and a flag ('Yes'/'No') indicating if it exceeds the average.
Related Concepts
Hint
This problem likely requires a multi-step approach using CTEs:
- Calculate Departmental Budgets: First, create a CTE to sum salaries for each department. Join
EmployeesandDepartments, thenGROUP BYdepartment ID and name, and useSUM(salary). - Calculate Overall Average Budget:
- Option A (Subquery/CTE): From the result of step 1, calculate the
AVG()of these departmental total budgets. This can be done in another CTE or a subquery. - Option B (Window Function): If you calculate departmental budgets, you can then use
AVG(total_department_budget) OVER ()in a subsequent step to get the overall average on each row.
- Option A (Subquery/CTE): From the result of step 1, calculate the
- Compare and Flag: Join the departmental budgets (from step 1) with the overall average budget (from step 2). Use a
CASEstatement to create the 'Yes'/'No' flag by comparing each department's total budget to the overall average.
Solution (PostgreSQL)
PostgreSQL Query (using CTEs)
-- Calculate total salary budget for each department and identify those exceeding the average
WITH DepartmentBudgets AS (
-- Step 1: Calculate total salary for each department
SELECT
d.department_id,
d.department_name,
SUM(e.salary) AS total_budget
FROM
Employees e
JOIN
Departments d ON e.department_id = d.department_id
GROUP BY
d.department_id,
d.department_name
),
AverageBudget AS (
-- Step 2: Calculate the average budget across all departments
SELECT
AVG(total_budget) AS avg_dept_budget
FROM
DepartmentBudgets
)
-- Step 3: Compare department budgets to the average and flag
SELECT
db.department_name,
db.total_budget,
CASE
WHEN db.total_budget > ab.avg_dept_budget THEN 'Yes'
ELSE 'No'
END AS exceeds_average
FROM
DepartmentBudgets db,
AverageBudget ab -- Cross join is fine here as AverageBudget has only one row
ORDER BY
db.department_name;
Explanation of the PostgreSQL Query:
DepartmentBudgets CTE:
- Purpose: Calculates the sum of salaries for each department.
SELECT d.department_id, d.department_name, SUM(e.salary) AS total_budget: Selects department identifiers and the sum of salaries.FROM Employees e JOIN Departments d ON e.department_id = d.department_id: Joins employees with their respective departments.GROUP BY d.department_id, d.department_name: Groups employees by department soSUM(e.salary)calculates the total for each one.
AverageBudget CTE:
- Purpose: Calculates the overall average of all the departmental total budgets computed in the previous CTE.
SELECT AVG(total_budget) AS avg_dept_budget FROM DepartmentBudgets: Takes all thetotal_budgetvalues from theDepartmentBudgetsCTE and calculates their average. This CTE will produce a single row with a single column (avg_dept_budget).
SELECT Statement:
- Purpose: Joins the individual department budgets with the overall average budget and determines if each department exceeds this average.
SELECT db.department_name, db.total_budget, ...: Selects the required output columns.FROM DepartmentBudgets db, AverageBudget ab: This performs aCROSS JOIN(implicitly, since there's no ON clause andAverageBudgethas one row). This effectively makes the singleavg_dept_budgetvalue available to compare against each department's budget fromdb.CASE WHEN db.total_budget > ab.avg_dept_budget THEN 'Yes' ELSE 'No' END AS exceeds_average: This creates the flag. If a department'stotal_budgetis strictly greater than theavg_dept_budget, it's flagged 'Yes'; otherwise, 'No'.ORDER BY db.department_name: Orders the final result alphabetically by department name for consistent output.
Alternative with Window Function for Average: Instead of a separate AverageBudget CTE, you could calculate the average using a window function directly after calculating department budgets if you prefer fewer CTEs (though the CTE approach is often clearer for multi-step calculations).
WITH DepartmentBudgets AS (
SELECT
d.department_id,
d.department_name,
SUM(e.salary) AS total_budget
FROM Employees e
JOIN Departments d ON e.department_id = d.department_id
GROUP BY d.department_id, d.department_name
)
SELECT
department_name,
total_budget,
CASE
WHEN total_budget > AVG(total_budget) OVER () THEN 'Yes'
ELSE 'No'
END AS exceeds_average
FROM DepartmentBudgets
ORDER BY department_name;
In this alternative, AVG(total_budget) OVER () calculates the average of total_budget across all rows produced by the DepartmentBudgets CTE, making it available on each row for direct comparison.