SQL REFERENCE

Aggregation

Aggregate functions compute a single result from a set of rows. Combined with GROUP BY, they produce one result per group.

Aggregate Functions

Function Description NULL Handling
COUNT(*)Number of rowsCounts all rows including NULLs
COUNT(col)Number of non-NULL valuesIgnores NULLs
SUM(col)Sum of valuesIgnores NULLs
AVG(col)Arithmetic meanIgnores NULLs
MIN(col)Minimum valueIgnores NULLs
MAX(col)Maximum valueIgnores NULLs

Basic Aggregation (No GROUP BY)

Without GROUP BY, aggregates operate on all rows and return a single-row result.

SELECT
  COUNT(*) AS total_employees,
  SUM(salary) AS total_payroll,
  AVG(salary) AS avg_salary,
  MIN(salary) AS min_salary,
  MAX(salary) AS max_salary
FROM employees;

Result:

total_employeestotal_payrollavg_salarymin_salarymax_salary
1501275000085000.0042000195000

GROUP BY (Single Key)

Groups rows by a column and computes aggregates per group.

SELECT
  department,
  COUNT(*) AS headcount,
  AVG(salary) AS avg_salary
FROM employees
GROUP BY department;

Result:

departmentheadcountavg_salary
Engineering45105000.00
Marketing3072000.00
Sales4068000.00
Research3595000.00

GROUP BY (Multiple Keys)

Group by multiple columns to produce finer-grained aggregates.

SELECT
  department,
  active,
  COUNT(*) AS headcount,
  SUM(salary) AS total_salary
FROM employees
GROUP BY department, active
ORDER BY department, active;

Result:

departmentactiveheadcounttotal_salary
Engineeringfalse5475000
Engineeringtrue404225000
Marketingfalse3198000
Marketingtrue271962000

COUNT Variations

-- Count all rows
SELECT COUNT(*) FROM employees;

-- Count non-NULL values
SELECT COUNT(manager_id) FROM employees;

-- Count distinct values
SELECT COUNT(DISTINCT department) FROM employees;

HAVING

The HAVING clause filters groups after aggregation. It is to GROUP BY what WHERE is to FROM.

-- Departments with average salary above 80k
SELECT
  department,
  AVG(salary) AS avg_salary,
  COUNT(*) AS headcount
FROM employees
GROUP BY department
HAVING AVG(salary) > 80000;

Result:

departmentavg_salaryheadcount
Engineering105000.0045
Research95000.0035
-- Departments with more than 10 active employees
SELECT department, COUNT(*) AS active_count
FROM employees
WHERE active = TRUE
GROUP BY department
HAVING COUNT(*) > 10
ORDER BY active_count DESC;

WHERE vs. HAVING

AspectWHEREHAVING
FiltersIndividual rowsGroups (after aggregation)
EvaluatedBefore GROUP BYAfter GROUP BY
Can use aggregates?NoYes
-- WHERE filters rows before grouping,
-- HAVING filters groups after aggregation
SELECT department, AVG(salary) AS avg_salary
FROM employees
WHERE active = TRUE              -- filter rows first
GROUP BY department
HAVING AVG(salary) > 80000       -- then filter groups
ORDER BY avg_salary DESC;

Aggregation with Expressions

-- Aggregate computed values
SELECT
  department,
  SUM(salary * 12) AS annual_payroll,
  AVG(salary * (1 + bonus_pct / 100)) AS avg_total_comp
FROM employees
GROUP BY department;

-- Conditional aggregation
SELECT
  department,
  SUM(CASE WHEN active THEN salary ELSE 0 END) AS active_payroll,
  SUM(CASE WHEN NOT active THEN salary ELSE 0 END) AS inactive_payroll
FROM employees
GROUP BY department;