PostgreSQL HAVING

Filter grouped results based on aggregate function conditions

🔍 What is HAVING?

HAVING filters groups created by GROUP BY based on aggregate function results. Unlike WHERE which filters individual rows, HAVING filters entire groups after aggregation is performed.


-- Basic HAVING syntax
SELECT column, aggregate_function(column)
FROM table
GROUP BY column
HAVING aggregate_function(column) condition;
                                    

Key HAVING Concepts

🎯

Filter Groups

Filter results after grouping

SELECT city, COUNT(*)
FROM customers
GROUP BY city
HAVING COUNT(*) > 5;
📊

Aggregate Conditions

Use aggregate functions in conditions

SELECT category, AVG(price)
FROM products
GROUP BY category
HAVING AVG(price) > 100;
⚖️

HAVING vs WHERE

WHERE filters rows, HAVING filters groups

-- WHERE: before grouping
-- HAVING: after grouping
SELECT city, COUNT(*)
FROM customers
WHERE active = true
GROUP BY city
HAVING COUNT(*) > 3;
🔗

Multiple Conditions

Combine multiple HAVING conditions

SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000 
   AND COUNT(*) > 5;

🔹 Basic HAVING Example

Find cities with more than 2 customers. HAVING filters the grouped results, showing only cities that meet the count condition after GROUP BY has organized the data.

-- Sample table
CREATE TABLE customers (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    city VARCHAR(50)
);

-- Insert sample data
INSERT INTO customers (name, city) VALUES
('John', 'Boston'),
('Jane', 'Boston'),
('Mike', 'Seattle'),
('Sarah', 'Boston'),
('Tom', 'Seattle');

-- HAVING query
SELECT 
    city,
    COUNT(*) AS customer_count
FROM customers
GROUP BY city
HAVING COUNT(*) > 2;

Output:

city customer_count
Boston 3

🔹 HAVING with SUM and AVG

Filter customers based on their total spending or average order value. HAVING lets you identify high-value customers by applying conditions to aggregated sales data.

-- Find customers with total purchases over $500
SELECT 
    customer_id,
    COUNT(*) AS order_count,
    SUM(amount) AS total_spent,
    AVG(amount) AS avg_order
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 500
ORDER BY total_spent DESC;

Output:

customer_id order_count total_spent avg_order
1 4 750.00 187.50
3 3 600.00 200.00

🔹 WHERE vs HAVING

WHERE filters individual rows before grouping, while HAVING filters groups after aggregation. You can use both together: WHERE narrows down rows first, then HAVING filters the grouped results.

-- Using both WHERE and HAVING
SELECT 
    category,
    COUNT(*) AS product_count,
    AVG(price) AS avg_price
FROM products
WHERE in_stock = true          -- Filter rows BEFORE grouping
GROUP BY category
HAVING AVG(price) > 50         -- Filter groups AFTER grouping
ORDER BY avg_price DESC;

Output:

category product_count avg_price
Electronics 8 299.99
Furniture 5 149.99

🔹 Multiple HAVING Conditions

Combine multiple conditions in HAVING clause using AND or OR operators. This allows complex filtering of grouped data based on multiple aggregate criteria simultaneously.

-- Find departments with specific criteria
SELECT 
    department,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary
FROM employees
GROUP BY department
HAVING COUNT(*) >= 5 
   AND AVG(salary) > 60000
   AND MAX(salary) < 150000
ORDER BY avg_salary DESC;

Output:

department employee_count avg_salary max_salary
Engineering 8 85000 120000
Sales 6 70000 95000

🔹 HAVING with MIN and MAX

Use HAVING with MIN and MAX functions to filter groups based on minimum or maximum values. This helps identify groups with specific range characteristics in your data.

-- Find product categories with price range criteria
SELECT 
    category,
    COUNT(*) AS product_count,
    MIN(price) AS lowest_price,
    MAX(price) AS highest_price
FROM products
GROUP BY category
HAVING MIN(price) > 20 
   AND MAX(price) < 1000;

Output:

category product_count lowest_price highest_price
Accessories 12 25.00 299.99
Clothing 20 29.99 199.99

🧠 Test Your Knowledge

What is the main difference between WHERE and HAVING?