Data & Analytics

SQL for Beginners: The Queries You Need to Know

Learn SELECT, WHERE, GROUP BY and JOIN using one realistic coffee shop sales table, with every query's real output shown so you can follow along.

Quick answer

SQL is the standard language for asking questions of data in a database. Beginners need about 8 building blocks to answer most business questions: SELECT, WHERE, ORDER BY, LIMIT, aggregate functions like SUM and COUNT, GROUP BY, HAVING and JOIN. You can learn and practice them free in an afternoon using SQLite or PostgreSQL.

SQL for Beginners: The Queries You Need to Know

Photo: Unsplash

On this page

Spreadsheets are great until your sales data runs to tens of thousands of rows or lives in a database. Then you need a faster way to ask questions. That’s what SQL is for.

SQL (Structured Query Language) is the standard language for working with relational databases. It’s defined by an international standard, ISO/IEC 9075, and used by PostgreSQL, MySQL, SQLite, SQL Server, Oracle and most cloud data warehouses. The good news for beginners: you only need a handful of commands to answer most business questions.

This guide teaches those commands using one realistic sales table from a small coffee shop with 2 locations. Every query below was run against this data, and the results shown are the real output.

Meet the sales table

Here’s the sales table we’ll use. Each row is one order line.

order_id order_date customer_id product category quantity unit_price store
1001 2026-09-01 1 Cold brew Drinks 2 4.50 Downtown
1002 2026-09-01 2 Croissant Bakery 3 3.25 Downtown
1003 2026-09-02 3 Latte Drinks 1 5.00 Airport
1004 2026-09-02 1 Bagel Bakery 2 2.75 Downtown
1005 2026-09-03 4 Cold brew Drinks 4 4.50 Airport
1006 2026-09-03 2 Latte Drinks 2 5.00 Downtown
1007 2026-09-04 5 Muffin Bakery 6 3.00 Airport
1008 2026-09-04 3 Cold brew Drinks 1 4.50 Airport
1009 2026-09-05 6 Croissant Bakery 2 3.25 Downtown
1010 2026-09-05 (empty) Latte Drinks 3 5.00 Airport

Order 1010 has no customer ID, which is realistic: not every buyer gives their details. That empty value is called NULL in SQL, and it will matter later.

Later we’ll add a small customers table to learn joins. To follow along, you can install SQLite (free, no server needed) or PostgreSQL, or use any online SQL playground.

SELECT: choosing what to see

Every query starts with SELECT (what columns you want) and FROM (which table).

sql SELECT order_id, product, quantity, unit_price FROM sales LIMIT 3;

Result:

order_id | product | quantity | unit_price 1001 | Cold brew | 2 | 4.50 1002 | Croissant | 3 | 3.25 1003 | Latte | 1 | 5.00

SELECT * returns every column. It’s handy for a quick look, but naming columns is better practice in real work. LIMIT 3 returns only 3 rows. Note that SQL Server uses SELECT TOP 3 instead, and the SQL standard also offers FETCH FIRST 3 ROWS ONLY.

To see unique values, use DISTINCT:

sql SELECT DISTINCT product FROM sales ORDER BY product;

This returns 5 products: Bagel, Cold brew, Croissant, Latte and Muffin.

Filtering and sorting: WHERE and ORDER BY

WHERE: filtering rows

WHERE keeps only the rows that match a condition. Here are all drink orders at the Airport store:

sql SELECT order_id, product, quantity FROM sales WHERE category = 'Drinks' AND store = 'Airport';

Result:

order_id | product | quantity 1003 | Latte | 1 1005 | Cold brew | 4 1008 | Cold brew | 1 1010 | Latte | 3

Useful operators include =, <> (not equal), >, <, BETWEEN, IN ('Latte', 'Muffin'), LIKE 'Cold%' for pattern matching, and IS NULL for empty values. Text values go in single quotes.

Calculations, aliases and ORDER BY

You can calculate new values on the fly. Revenue for each order line is quantity times unit price:

sql SELECT order_id, product, quantity * unit_price AS revenue FROM sales ORDER BY revenue DESC, order_id LIMIT 3;

Result:

order_id | product | revenue 1005 | Cold brew | 18.00 1007 | Muffin | 18.00 1010 | Latte | 15.00

AS revenue gives the calculated column a name, called an alias. ORDER BY revenue DESC sorts from highest to lowest. Adding order_id as a second sort key breaks ties in a predictable way, because without it the database can return tied rows in any order.

Summarizing data: aggregates, GROUP BY and HAVING

Aggregate functions

Aggregate functions collapse many rows into one answer. The 5 you’ll use constantly are COUNT, SUM, AVG, MIN and MAX.

sql SELECT COUNT(*) AS orders, SUM(quantity) AS units, SUM(quantity * unit_price) AS revenue, AVG(quantity * unit_price) AS avg_order FROM sales;

Result:

orders | units | revenue | avg_order 10 | 26 | 101.25 | 10.125

In one query you have total orders, units sold, revenue and average order value. Use ROUND(AVG(...), 2) to tidy decimals.

GROUP BY: totals by product or store

GROUP BY is where SQL starts to feel powerful. It splits rows into groups and runs the aggregate on each group. Here’s revenue by product:

sql SELECT product, SUM(quantity) AS units, SUM(quantity * unit_price) AS revenue FROM sales GROUP BY product ORDER BY revenue DESC;

Result:

product | units | revenue Cold brew | 7 | 31.50 Latte | 6 | 30.00 Muffin | 6 | 18.00 Croissant | 5 | 16.25 Bagel | 2 | 5.50

Cold brew is the top earner, and bagels barely register.

You can group by more than one column. Revenue by store and category:

sql SELECT store, category, SUM(quantity * unit_price) AS revenue FROM sales GROUP BY store, category ORDER BY store, revenue DESC;

Result:

store | category | revenue Airport | Drinks | 42.50 Airport | Bakery | 18.00 Downtown | Bakery | 21.75 Downtown | Drinks | 19.00

Drinks dominate at the Airport, while Downtown sells more bakery items.

Key takeaway: The rule for GROUP BY is simple: every column in your SELECT must either be in the GROUP BY or wrapped in an aggregate function like SUM or COUNT. Most beginner errors come from breaking this rule.

HAVING: filtering groups

WHERE filters rows before grouping. HAVING filters groups after the aggregates are calculated. To find products ordered at least 3 times:

sql SELECT product, COUNT(*) AS orders FROM sales GROUP BY product HAVING COUNT(*) >= 3;

Result:

product | orders Cold brew | 3 Latte | 3

If a condition doesn’t involve an aggregate, put it in WHERE. The PostgreSQL documentation notes that this is more efficient, because rows that fail the check are removed before any grouping work.

JOIN: combining tables

Real databases split data into several tables. Customer details usually live in their own table, linked to sales by customer_id. Here’s our customers table:

customer_id name city loyalty_member
1 Ana Lopez Austin Yes
2 Ben Carter Austin No
3 Chloe Kim Dallas Yes
4 Dev Patel Houston No
5 Emma Brown Austin Yes
7 Frank Diaz Dallas No

Notice 2 mismatches, as in real life: customer 6 appears in sales but not in this table, and Frank Diaz (7) has never ordered.

INNER JOIN: only matching rows

sql SELECT s.order_id, c.name, s.product, s.quantity * s.unit_price AS revenue FROM sales AS s INNER JOIN customers AS c ON s.customer_id = c.customer_id ORDER BY s.order_id;

This returns 8 rows, orders 1001 through 1008, each with the customer’s name. Orders 1009 and 1010 disappear because they have no matching customer. The short names s and c are table aliases that save typing.

LEFT JOIN: keep everything from the first table

A LEFT JOIN keeps every row from the left table, filling in NULL where there’s no match. That makes it perfect for finding gaps:

sql SELECT s.order_id, s.customer_id, c.name FROM sales AS s LEFT JOIN customers AS c ON s.customer_id = c.customer_id WHERE c.customer_id IS NULL;

Result:

order_id | customer_id | name 1009 | 6 | NULL 1010 | NULL | NULL

Order 1009 points to a customer missing from your customer table, a data quality problem worth fixing. Flip the tables and the same pattern finds customers who have never ordered, which returns Frank Diaz, a candidate for a welcome offer.

JOIN plus GROUP BY: the everyday combo

Revenue by customer city:

sql SELECT c.city, COUNT(*) AS orders, SUM(s.quantity * s.unit_price) AS revenue FROM sales AS s INNER JOIN customers AS c ON s.customer_id = c.customer_id GROUP BY c.city ORDER BY revenue DESC;

Result:

city | orders | revenue Austin | 5 | 52.25 Houston | 1 | 18.00 Dallas | 2 | 9.50

Join types at a glance

Join type What it returns Typical use
INNER JOIN Only rows with a match in both tables Sales with customer details
LEFT JOIN All rows from the left table, plus matches from the right Find orders with missing customers, or customers with no orders
RIGHT JOIN All rows from the right table, plus matches from the left Same as LEFT JOIN with tables swapped; less common
FULL OUTER JOIN All rows from both tables, matched where possible Reconciling 2 lists, such as POS and accounting records

SQLite added RIGHT and FULL OUTER JOIN support in version 3.39.0, so older tools may only support INNER and LEFT joins.

The order SQL actually runs your query

You write SELECT first, but the database logically processes clauses in a different order. Knowing it explains many confusing errors:

  1. FROM and JOIN: gather the tables
  2. WHERE: filter rows
  3. GROUP BY: form groups
  4. HAVING: filter groups
  5. SELECT: pick and calculate columns
  6. ORDER BY: sort
  7. LIMIT: cut to the number of rows

That’s why you generally can’t use a column alias from SELECT inside WHERE: the alias doesn’t exist yet at that step.

Where to start: a 1-week practice plan

  1. Day 1: Install SQLite and a free viewer such as DB Browser for SQLite, or use an online playground. Create the sales table above.
  2. Day 2: Practice SELECT, WHERE and ORDER BY. Write 10 queries answering simple questions, such as “Which orders were over $10?”
  3. Day 3: Practice aggregates and GROUP BY: revenue by day, store and category.
  4. Day 4: Add HAVING and learn to explain the difference from WHERE out loud.
  5. Day 5: Create the customers table and practice INNER JOIN and LEFT JOIN.
  6. Day 6: Export a real report from your POS or ecommerce platform to CSV, import it and repeat the week’s queries on your own data.
  7. Day 7: Write 5 business questions and answer each with one query.

AI assistants can help write and explain queries, but check every result against a number you already know. Our guide on how to use AI in Excel and Google Sheets covers similar habits for spreadsheets.

Conclusion

SQL looks intimidating, but the core is small. SELECT and WHERE pick and filter data, aggregates and GROUP BY summarize it, HAVING filters the summaries, and JOIN connects tables. With those 8 building blocks you can answer most of the questions a business owner or analyst asks every week. Practice on a small table like this one, then move to your own data.

Ready to go further? See how SQL fits into data analytics for small business and retail analytics, browse free resources in how to learn to code for free, or plan a career path with how to become a data analyst.

Sources

Questions readers ask

Frequently asked questions

Is SQL hard to learn for beginners?

No, SQL is one of the easier technical skills to start. Its commands read almost like English, such as SELECT product FROM sales WHERE store = 'Airport'. Most beginners can write useful queries with SELECT, WHERE, GROUP BY and JOIN within a few days of practice. Advanced topics like window functions and query tuning take longer, but you don't need them to start answering business questions.

What are the basic SQL commands I should learn first?

Learn these in order: SELECT and FROM to pick data, WHERE to filter rows, ORDER BY and LIMIT to sort and trim results, aggregate functions like COUNT, SUM and AVG, GROUP BY to total by category, HAVING to filter grouped results and JOIN to combine tables. Those 8 building blocks cover most everyday analysis. Data-changing commands like INSERT and UPDATE come next.

What is the difference between WHERE and HAVING?

WHERE filters individual rows before they're grouped, while HAVING filters groups after aggregate functions are calculated. Use WHERE for conditions like store = 'Airport' and HAVING for conditions like COUNT(*) >= 3 or SUM(revenue) > 1000. If a condition doesn't use an aggregate, put it in WHERE, because it's more efficient and easier to read.

What is the difference between INNER JOIN and LEFT JOIN?

An INNER JOIN returns only rows that have a match in both tables, so orders without a matching customer disappear. A LEFT JOIN returns every row from the first table and fills in NULL where the second table has no match. Use INNER JOIN to combine related data, and LEFT JOIN to find gaps, such as customers who have never ordered.

Which SQL database should a beginner use?

SQLite is a great first choice because it's free, needs no server and stores a whole database in a single file. PostgreSQL is a strong next step and widely used in industry. MySQL and SQL Server are also common at work. The basic commands in this guide work almost identically across all of them, with small differences like LIMIT versus TOP.

Do I need SQL to become a data analyst?

In most cases, yes. SQL is a core skill for data analyst roles, because company data usually lives in databases and warehouses. You'll also use spreadsheets and a visualization tool, but SQL is how you pull and shape the data. Learning SELECT, GROUP BY and JOIN well is a strong foundation for an analyst career.

Can I practice SQL for free?

Yes. You can install SQLite or PostgreSQL free on your own computer, use DB Browser for SQLite as a visual tool, or use free online SQL playgrounds in your browser. The best practice data is your own: export a CSV of sales or orders from your POS or ecommerce platform, import it and try to answer real questions about your business.

Reader reviews

Was this guide useful

No reviews yet. Share your experience and help other readers.

Write a review
Your rating
Never shown publicly

Reviews are checked before they appear. We never show your email. Please review only places or services you have used.

Manishdai
Written by

Manishdai

Manishdai writes about the technology that is reshaping every industry — from AI agents and cloud platforms to healthtech, fintech and the IT companies building it all across the United States.