WHERE Filters Before, HAVING Filters After
WHERE applies to raw rows before grouping, while HAVING applies to the aggregated result. Filtering a SUM() within a WHERE clause always causes an error: the sum does not yet exist at that stage of execution.
Every Non-Aggregated Column Must Be Grouped
If you select customer_name and SUM(amount), customer_name must appear in the GROUP BY clause. PostgreSQL will reject the query, while MySQL in permissive mode might return an arbitrary value—which is worse, as the error becomes silent.
Counting Correctly
COUNT(*) counts rows, COUNT(column) ignores NULLs, and COUNT(DISTINCT column) counts unique values. Three different results for three different business needs.
Readability
Name your aggregates using AS (e.g., SUM(amount) AS revenue): CSV exports and reporting tools use these aliases as column headers.

