In a per-department salary query, how does WHERE salary > 50000 differ from HAVING AVG(salary) > 50000?
answer
- Ask what the condition is true of: a person or a department
- One filter changes the rows that feed the average
- The other only accepts or rejects finished averages
- Try a department with one huge salary and several small ones
basics
~20 sWHERE drops low-paid employees before grouping, so a department appears if it has any high earner and its reported average covers only those employees. HAVING keeps everyone in the average and returns only departments whose overall average exceeds 50000.
solid answer
~40 sThey answer different questions, and they differ in both the rows returned and the numbers reported. `WHERE salary > 50000` removes individual employees before `GROUP BY` runs, so each department that still has at least one qualifying employee appears, and its `AVG(salary)` is the average **of the high earners only** — a number that is always above 50000 by construction. `HAVING AVG(salary) > 50000` leaves every employee in the group, computes the department's true average, and then keeps only the departments above the threshold. A department of mostly low salaries with one large one can therefore appear under `WHERE` and vanish under `HAVING`. Both can also be combined, in which case `WHERE` applies first and the `HAVING` average is computed over the surviving rows only — which is a third, different result.
code
sql · 12 lines-- departments containing at least one employee paid over 50000;
-- the reported average covers ONLY those employees
SELECT dept, AVG(salary) AS avg_salary
FROM employees
WHERE salary > 50000
GROUP BY dept;
-- departments whose average over ALL employees exceeds 50000
SELECT dept, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept
HAVING AVG(salary) > 50000;go deeper
Recall the test to apply: if the condition is true or false of a single employee it belongs in WHERE; if it needs a whole department's average or count it belongs in HAVING.
Show with a small data set that the two forms differ in both the departments returned and the averages reported, and explain that WHERE changes what feeds the aggregate while HAVING only accepts or rejects it.
Treat ambiguous requirements as the real risk: pin down whether a stakeholder means 'has a high earner' or 'pays well on average', and keep reported figures reconcilable with the source population.
Own the definitions layer — metric semantics such as which population an average covers should be fixed once in shared views or a semantic model rather than re-decided in each analyst's query.
## Two English questions, two SQL clauses "Show me departments with high earners" and "show me departments that pay well on average" sound close in English and are entirely different queries. The first is a row filter, the second a group filter. ```sql -- A: departments containing at least one employee paid over 50000 SELECT dept, AVG(salary) AS avg_salary FROM employees WHERE salary > 50000 GROUP BY dept; -- B: departments whose average salary exceeds 50000 SELECT dept, AVG(salary) AS avg_salary FROM employees GROUP BY dept HAVING AVG(salary) > 50000; ``` ## Working through data Take three departments: - Sales: 90000 and 30000 — true average 60000 - IT: 60000 and 40000 — true average 50000 - HR: 40000 and 30000 — true average 35000 Query B returns Sales with 60000. IT's average is exactly 50000, which is not greater than 50000, and HR is far below. Query A first deletes every row at or below 50000, leaving one Sales row (90000) and one IT row (60000) and nothing for HR. It then returns Sales with 90000 and IT with 60000. So the two queries disagree about which departments qualify (IT appears in one and not the other) **and** about Sales's average (90000 versus 60000). Nothing about the data changed; only the stage at which the predicate was applied. ## The general rule `WHERE` changes which rows feed the aggregates, so it changes the aggregate values themselves. `HAVING` never changes a value — it only decides whether an already-computed group survives. Any time a filter moves across the grouping boundary, expect the numbers to move with it. A related trap is the average-of-a-filtered-set being self-fulfilling: once you have kept only salaries above 50000, `AVG(salary) > 50000` is guaranteed true and tells you nothing. Candidates who write `WHERE salary > 50000 ... HAVING AVG(salary) > 50000` and believe they have applied two checks have applied one and a tautology. ## Combining them deliberately Using both is legitimate when each expresses a genuinely different intent: ```sql -- among full-time staff only, departments averaging over 50000 SELECT dept, AVG(salary) AS avg_salary, COUNT(*) AS headcount FROM employees WHERE employment_type = 'FULL_TIME' GROUP BY dept HAVING AVG(salary) > 50000 AND COUNT(*) >= 5; ``` Read it as a pipeline: `WHERE` defines the population, `GROUP BY` defines the unit of reporting, aggregates summarise the population per unit, `HAVING` picks the units worth showing. Here `COUNT(*) >= 5` suppresses departments too small for the average to mean anything — another predicate that only `HAVING` can express. ## Why the wrong one is chosen The mistake is usually a translation error rather than a syntax one: the requirement says "departments where salary is above 50000", which is ambiguous, and the developer picks whichever clause they habitually reach for. The defence is to ask which object the condition describes. If the sentence is true or false of one employee, it is a `WHERE` predicate. If it is only true or false of a whole department — average, total, headcount, maximum — it is a `HAVING` predicate. ## Reporting the honest number When you want to *find* departments by a filtered criterion but *report* the unfiltered figure, do not move the filter to `WHERE`; keep the population intact and express the condition as a conditional aggregate instead, so the reported average still covers everyone. Mixing the two intentions in a single `WHERE` clause is how reports end up with averages that no one can reconcile against the source data. ## What a strong answer sounds like Name both effects — different set of departments, different averages — give a two-department example where they disagree, and add that `WHERE` runs first when both clauses are present, so the `HAVING` aggregate is computed over the filtered rows rather than the full table.
- What does WHERE salary > 50000 combined with HAVING AVG(salary) > 50000 in one query actually test?Effectively only the WHERE condition. Once every remaining salary is above 50000, their average is necessarily above 50000, so the HAVING predicate is a tautology and removes nothing. It looks like a second safeguard but adds no filtering — a good example of why you must know which rows an aggregate is computed over.
- You need departments that have at least one employee over 50000, but you must report each department's true average. How?Keep every row in the group so the average stays honest, and express the condition as a group-level test on the full population: GROUP BY dept HAVING MAX(salary) > 50000. MAX is computed over all employees, so the department qualifies on its top earner while AVG(salary) still reflects everyone.
- If both WHERE and HAVING appear, which rows does the HAVING aggregate cover?Only the rows that passed WHERE. WHERE runs first, GROUP BY forms groups from the survivors, and the aggregates summarise exactly those rows. So a HAVING threshold is always a statement about the filtered population, never about the whole table — something to state explicitly when a report's definition is being agreed.
saying these in an interview costs you the question
- Says the two queries return the same departments
- Believes only the row set differs, not the averages
- Adds both filters thinking it doubles the check
- Reads 'departments where salary > 50000' as unambiguous
- Assumes HAVING recomputes over unfiltered rows