Grouping Data with GROUP BY: PostgreSQL Analytics for Beginners
Master the GROUP BY clause to transform raw store data into actionable insights. Learn to aggregate sales by customer and category in PostgreSQL.

Previously in this course, we explored Introduction to Aggregate Functions: SQL Math for Analytics, where we calculated totals and averages across entire tables. While helpful, knowing the "total revenue" of your store only tells half the story; to run a business effectively, you need to break that data down by specific segments.
In this lesson, we will use the GROUP BY clause to perform data summarization, allowing us to see how individual customers and specific product categories are actually performing.
Understanding Data Summarization with GROUP BY
Think of GROUP BY as a way to "bucket" your rows based on shared values. When you use an aggregate function like SUM() or COUNT() without a GROUP BY clause, PostgreSQL collapses your entire table into a single result row. By adding GROUP BY, you instruct PostgreSQL to create a separate bucket for every unique value in the column(s) you specify, calculating the aggregate separately for each bucket.
Aggregating Sales per Customer
In our store schema, we have an orders table linked to customers via a customer_id. To understand which customers are our "whales"—those who spend the most money—we need to group our order records by the customer.
SQLSELECT customer_id, SUM(total_amount) AS total_spent FROM orders GROUP BY customer_id;
When you execute this, PostgreSQL:
- Scans the
orderstable. - Identifies every unique
customer_id. - Sums the
total_amountfor each specific ID. - Returns a list of customers alongside their individual spending totals.
Aggregating per Product Category
Similarly, you can analyze product performance. Suppose you have a products table with a category column. To see which category generates the most revenue, you join your order_items (containing individual product sales) with products and group by the category:
SQLSELECT p.category, COUNT(oi.id) AS units_sold, SUM(oi.price * oi.quantity) AS category_revenue FROM order_items oi JOIN products p ON oi.product_id = p.id GROUP BY p.category;
This query transforms a long list of individual transaction lines into a clean executive summary of which store departments are pulling their weight.
Hands-on Exercise: Analyze Your Store

Using the order_items table from your running project, perform the following two tasks:
- Customer Activity: Write a query that shows how many items each
order_idcontains. (Hint: Group byorder_idand useCOUNT(*)). - Category Performance: Group your
order_itemsbyproduct_idto see the total quantity sold for every specific product in your inventory.
Common Pitfalls
- The "Non-Aggregated" Error: A classic beginner mistake is including a column in the
SELECTlist that isn't in theGROUP BYclause. For example:SELECT customer_id, order_date, SUM(total) FROM orders GROUP BY customer_id;will fail. PostgreSQL doesn't know whichorder_dateto show if a customer has multiple orders. Every column in yourSELECTmust either be in theGROUP BYor wrapped in an aggregate function. - Forgetting the Group: If you perform an aggregate function but forget to add the
GROUP BYclause, you will receive a syntax error indicating that you are mixing aggregate and non-aggregate columns. - NULL Values: Remember that
GROUP BYtreatsNULLas a distinct group. If some of your records have aNULLcategory, they will appear as their own row in your results.
FAQ
Q: Can I group by multiple columns?
A: Yes. You can use GROUP BY category, sub_category to create sub-total buckets within larger categories.
Q: Does the order of columns in GROUP BY matter?
A: Logically, no, but it does affect the sort order of the result set if you aren't using an explicit ORDER BY clause.
Q: Is GROUP BY faster than doing multiple queries?
A: Absolutely. Processing the entire table in one pass with GROUP BY is significantly more efficient than running separate queries for every category or customer.
Recap
Data summarization is the heartbeat of business intelligence. By using GROUP BY, you move from seeing raw, overwhelming transaction logs to understanding the trends, habits, and revenue drivers that define your store's success. Always ensure that your SELECT columns align with your GROUP BY buckets to keep your queries error-free.
Up next: Filtering Groups with HAVING — we will learn how to filter the results of your aggregations.
Work with me

Laravel SaaS MVP & Multi-Tenant App Development
Launch your SaaS MVP on Laravel — multi-tenant, subscription-ready, and built by the engineer behind a platform serving 10,000+ paying users.

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time — content pipelines, data workflows, and agentic AI tasks that run themselves.


