PostgreSQL Aggregate Functions

Calculate summary statistics from your data

📊 What are Aggregate Functions?

Aggregate functions perform calculations on multiple rows and return a single result. They help you find totals, averages, counts, and other summary statistics from your data efficiently.


-- Basic aggregate examples
SELECT COUNT(*) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT SUM(amount) FROM orders;
                                    

Output:

COUNT: 150

AVG: 65000.00

SUM: 125000.00

Common Aggregate Functions

🔢

COUNT

Count number of rows

SELECT COUNT(*) FROM users;

SUM

Calculate total sum

SELECT SUM(price) FROM products;
📈

AVG

Calculate average value

SELECT AVG(rating) FROM reviews;
⬆️

MAX / MIN

Find highest or lowest

SELECT MAX(salary) FROM employees;

🔹 COUNT()

Count the number of rows or non-NULL values in a result set. COUNT(*) counts all rows including NULLs, while COUNT(column) counts only non-NULL values. Essential for understanding data volume and completeness.

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

-- Count non-NULL values
SELECT COUNT(email) FROM employees;
-- Returns: 145

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

-- Count with condition
SELECT 
    COUNT(*) AS total_orders,
    COUNT(CASE WHEN status = 'completed' THEN 1 END) AS completed_orders
FROM orders;
                            

Output:

total_orders completed_orders
500 425

🔹 SUM()

Calculate the total sum of numeric values. SUM adds up all non-NULL values in a column. Perfect for calculating totals like revenue, quantities, or any cumulative numeric data.

-- Sum all values
SELECT SUM(amount) FROM orders;
-- Returns: 125000.00

-- Sum with condition
SELECT SUM(amount) FROM orders
WHERE status = 'completed';
-- Returns: 98000.00

-- Multiple sums
SELECT 
    SUM(amount) AS total_revenue,
    SUM(cost) AS total_cost,
    SUM(amount - cost) AS total_profit
FROM orders;
                            

Output:

total_revenue total_cost total_profit
125000.00 75000.00 50000.00

🔹 AVG()

Calculate the average (mean) of numeric values. AVG sums all non-NULL values and divides by the count. Useful for finding typical values, performance metrics, or central tendencies in your data.

-- Calculate average
SELECT AVG(salary) FROM employees;
-- Returns: 65000.00

-- Average with rounding
SELECT ROUND(AVG(rating), 2) FROM reviews;
-- Returns: 4.35

-- Average by group
SELECT 
    department,
    ROUND(AVG(salary), 2) AS avg_salary,
    ROUND(AVG(years_experience), 1) AS avg_experience
FROM employees
GROUP BY department;
                            

Output:

department avg_salary avg_experience
Engineering 75000.00 5.2

🔹 MAX() and MIN()

Find the maximum or minimum value in a set. MAX returns the largest value, MIN returns the smallest. Works with numbers, dates, and text. Essential for finding extremes, ranges, or boundaries in your data.

-- Find maximum
SELECT MAX(salary) FROM employees;
-- Returns: 150000.00

-- Find minimum
SELECT MIN(salary) FROM employees;
-- Returns: 35000.00

-- Find range
SELECT 
    MAX(price) AS highest_price,
    MIN(price) AS lowest_price,
    MAX(price) - MIN(price) AS price_range
FROM products;

-- Latest and earliest dates
SELECT 
    MAX(order_date) AS latest_order,
    MIN(order_date) AS first_order
FROM orders;
                            

Output:

MAX: 150000.00

MIN: 35000.00

🔹 GROUP BY with Aggregates

Combine aggregate functions with GROUP BY to calculate statistics for each group. This powerful combination lets you analyze data by categories, time periods, or any grouping criteria you define.

-- Group by single column
SELECT 
    department,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department;

-- Group by multiple columns
SELECT 
    department,
    job_title,
    COUNT(*) AS count,
    ROUND(AVG(salary), 2) AS avg_salary
FROM employees
GROUP BY department, job_title;

-- Group by date
SELECT 
    DATE_TRUNC('month', order_date) AS month,
    COUNT(*) AS order_count,
    SUM(amount) AS monthly_revenue
FROM orders
GROUP BY month
ORDER BY month;
                            

Output:

department employee_count avg_salary
Engineering 45 75000.00
Sales 30 55000.00

🔹 HAVING Clause

Filter grouped results using aggregate conditions. HAVING works like WHERE but for grouped data. Use it after GROUP BY to filter groups based on aggregate calculations like counts or averages.

-- Filter groups by count
SELECT 
    department,
    COUNT(*) AS employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 10;

-- Filter by average
SELECT 
    department,
    ROUND(AVG(salary), 2) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 60000;

-- Multiple conditions
SELECT 
    category,
    COUNT(*) AS product_count,
    AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING COUNT(*) >= 5 AND AVG(price) < 100;
                            

Output:

department employee_count
Engineering 45
Sales 30

🔹 STRING_AGG()

Concatenate values from multiple rows into a single string with a delimiter. STRING_AGG is perfect for creating comma-separated lists, combining tags, or aggregating text data from related records.

-- Concatenate with comma
SELECT STRING_AGG(name, ', ') AS employee_names
FROM employees
WHERE department = 'Engineering';
-- Returns: John, Alice, Bob, Sarah

-- Concatenate with custom delimiter
SELECT 
    department,
    STRING_AGG(name, ' | ' ORDER BY name) AS employees
FROM employees
GROUP BY department;

-- Create tag lists
SELECT 
    product_id,
    STRING_AGG(tag_name, ', ') AS tags
FROM product_tags
GROUP BY product_id;
                            

Output:

Result: John, Alice, Bob, Sarah

🔹 ARRAY_AGG()

Collect values from multiple rows into a PostgreSQL array. ARRAY_AGG creates an array containing all values from the grouped rows. Useful for JSON generation or when you need to work with sets of values.

-- Create array of values
SELECT ARRAY_AGG(name) AS employee_array
FROM employees
WHERE department = 'Engineering';
-- Returns: {John, Alice, Bob, Sarah}

-- Array with ordering
SELECT 
    department,
    ARRAY_AGG(name ORDER BY salary DESC) AS employees_by_salary
FROM employees
GROUP BY department;

-- Collect IDs
SELECT 
    customer_id,
    ARRAY_AGG(order_id) AS order_ids
FROM orders
GROUP BY customer_id;
                            

Output:

Result: {John, Alice, Bob, Sarah}

🔹 BOOL_AND() and BOOL_OR()

Perform logical AND or OR operations across multiple boolean values. BOOL_AND returns true only if all values are true, BOOL_OR returns true if any value is true. Great for checking conditions across groups.

-- Check if all are true
SELECT BOOL_AND(is_active) AS all_active
FROM employees
WHERE department = 'Engineering';
-- Returns: true or false

-- Check if any are true
SELECT BOOL_OR(has_certification) AS any_certified
FROM employees
WHERE department = 'Engineering';

-- Group checks
SELECT 
    department,
    BOOL_AND(is_active) AS all_active,
    BOOL_OR(is_manager) AS has_manager
FROM employees
GROUP BY department;
                            

Output:

BOOL_AND: true

BOOL_OR: true

🧠 Test Your Knowledge

What does AVG() calculate?