Complete SQL Training Guide for Beginners to Experts

What This Guide Covers

This is a complete SQL training resource. It goes from absolute basics to advanced queries you'll actually use on the job. No academic fluff. No theory that never applies. Just SQL that works.

SQL is the language for talking to databases. Every app, every website, every analytics dashboard relies on it. If you touch data, you need SQL. Period.

Getting Started: Your First Query

Before writing queries, you need somewhere to run them. Options:

Download one. Install it. Create a test database. That's your playground for the rest of this guide.

The Absolute Basics

Every SQL query follows the same pattern. Start here:

SELECT column_name FROM table_name;

This pulls data from a table. That's it. That's the foundation.

Want multiple columns?

SELECT column1, column2, column3 FROM table_name;

Want everything?

SELECT * FROM table_name;

The asterisk means "all columns." Use it when exploring. Avoid it in production code — selecting only what you need is faster and clearer.

Filtering Data

Pulling entire tables is useless. You need to filter. That's what the WHERE clause does.

SELECT name, email FROM users WHERE age >= 18;

This pulls name and email from users, but only rows where age is 18 or higher.

Comparison Operators

Combining Conditions

Need multiple filters? Use AND and OR.

SELECT * FROM orders 
WHERE status = 'shipped' AND total > 100;

OR gives you more matches:

SELECT * FROM users 
WHERE country = 'USA' OR country = 'Canada';

Watch your logic. AND runs before OR. Use parentheses to control the order:

SELECT * FROM users 
WHERE (country = 'USA' OR country = 'Canada') AND age > 25;

IN and BETWEEN

Instead of chaining ORs, use IN:

SELECT * FROM products WHERE category IN ('electronics', 'books', 'clothing');

BETWEEN grabs a range:

SELECT * FROM sales WHERE amount BETWEEN 50 AND 200;

Pattern Matching with LIKE

Need to match text patterns? LIKE does that.

SELECT * FROM users WHERE email LIKE '%@gmail.com';

The % is a wildcard. It matches anything before @gmail.com.

SELECT * FROM products WHERE name LIKE 'iPhone _';

This matches "iPhone 12", "iPhone 13", etc.

Sorting and Limiting Results

ORDER BY sorts your results:

SELECT name, price FROM products ORDER BY price DESC;

DESC means descending (highest first). Omit it or use ASC for ascending (lowest first).

LIMIT restricts how many rows come back:

SELECT * FROM users ORDER BY created_at DESC LIMIT 10;

This gets the 10 most recent users. Common pattern. Remember it.

Aggregation Functions

Aggregate functions crunch numbers across rows. The big four:

SELECT COUNT(*) FROM orders WHERE status = 'completed';
SELECT AVG(price) FROM products WHERE category = 'electronics';

GROUP BY

Aggregates alone give you one number. GROUP BY breaks it down by category:

SELECT category, COUNT(*) as product_count, AVG(price) as avg_price
FROM products
GROUP BY category;

This tells you how many products and the average price per category. The AS keyword creates an alias — a nickname for the column in your results.

HAVING

WHERE filters rows before grouping. HAVING filters groups after:

SELECT category, COUNT(*) as count
FROM products
GROUP BY category
HAVING count > 5;

This only shows categories with more than 5 products.

Quick note: WHERE comes before GROUP BY. HAVING comes after. Don't mix them up.

Joins: Combining Tables

Real data lives across multiple tables. Joins let you pull it together.

INNER JOIN

Returns rows that have matches in both tables:

SELECT orders.id, users.name, orders.total
FROM orders
INNER JOIN users ON orders.user_id = users.id;

This pulls order details with customer names. The ON clause specifies how the tables connect.

LEFT JOIN

Returns all rows from the left table, plus matches from the right. Non-matching right rows get NULL:

SELECT users.name, orders.id
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

This shows every user, even those who never placed an order. Their order columns will be empty.

RIGHT JOIN and FULL OUTER JOIN

Right join is the reverse of left join. Full outer join returns everything from both tables. Not every database supports them. PostgreSQL does. MySQL doesn't.

Multiple Joins

Chain joins to connect more tables:

SELECT orders.id, users.name, products.title
FROM orders
INNER JOIN users ON orders.user_id = users.id
INNER JOIN order_items ON orders.id = order_items.order_id
INNER JOIN products ON order_items.product_id = products.id;

Three tables connected in one query. This is normal. Get comfortable with it.

Subqueries

A subquery is a query inside another query. Use them when you can't get the answer in one step.

SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

This finds employees earning above average. The inner query runs first, then the outer query uses that result.

Subqueries work in WHERE, FROM, and SELECT clauses. They can be correlated (reference the outer query) or uncorrelated (standalone). Correlated subqueries are slower. Sometimes a JOIN is faster. Test both.

Modifying Data

SQL isn't just for reading. You can insert, update, and delete.

INSERT

INSERT INTO users (name, email, age) VALUES ('John Doe', 'john@example.com', 28);

Always specify columns. It's safer and easier to read.

UPDATE

UPDATE users SET age = 29 WHERE id = 5;

Always include a WHERE clause. Forget it, and you update every row in the table. That's a disaster. Trust me.

DELETE

DELETE FROM users WHERE id = 5;

Same rule. WHERE clause or you wipe the table. In production, wrap destructive queries in transactions until you're sure they work.

Creating Tables

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE,
    age INTEGER,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Common constraints:

  • PRIMARY KEY — unique identifier for each row
  • NOT NULL — column cannot be empty
  • UNIQUE — no duplicate values allowed
  • DEFAULT — value if none provided
  • FOREIGN KEY — references another table

Data types vary by database. SQLite has INTEGER, TEXT, REAL, BLOB. PostgreSQL and MySQL have more options: VARCHAR, BOOLEAN, TIMESTAMP, JSON, etc.

Common Mistakes Beginners Make

  • Forgetting WHERE on UPDATE/DELETE — test with SELECT first, then swap in your modifier
  • Using OR when IN would work — IN is cleaner and sometimes faster
  • Selecting * in production — name your columns explicitly
  • Ignoring NULLs — NULL is not zero, not empty string. It means "unknown." Use IS NULL or IS NOT NULL to check it
  • Not indexing — slow queries on big tables? Probably missing an index
  • Forgetting to commit — in databases with transaction support, changes aren't permanent until you COMMIT

SQL Tools Compared

ToolBest ForCostLearning Curve
SQLiteLocal development, small apps, learningFreeLow
PostgreSQLProduction apps, complex data, analyticsFreeMedium
MySQLWeb apps, WordPress, LAMP stackFree/PremiumLow
SQL ServerWindows environments, enterprisePaidMedium
BigQueryMassive datasets, cloud analyticsPay per queryMedium
DB BrowserVisual SQL work, beginnersFreeVery Low
DBeaverMulti-database managementFree/PremiumLow

Pick based on your goal. Learning SQL syntax? SQLite. Job in data? PostgreSQL. Building web apps? MySQL or PostgreSQL.

Practice Problems to Build Skill

Reading this guide isn't enough. You need to write queries. Here are problems that actually test your skills:

  1. Find the top 5 users by total order value
  2. List products that have never been ordered (LEFT JOIN with NULL check)
  3. Calculate month-over-month revenue growth
  4. Find duplicate email addresses in the users table
  5. Rank customers by purchase frequency

Struggling? That's normal. Break the problem down. What tables do you need? How do they connect? What filter gets you close? Build the query piece by piece.

Where to Practice

  • SQLZoo — interactive exercises, browser-based
  • LeetCode — SQL practice problems, common in interviews
  • Mode Analytics SQL Tutorial — free, good explanations
  • HackerRank — SQL track with difficulty levels
  • Your own database — import a dataset and explore it

What's Next

You know the fundamentals. Here's what separates intermediate from advanced SQL:

  • Window functions (ROW_NUMBER, RANK, LAG, LEAD)
  • Common Table Expressions (CTEs with WITH)
  • Complex joins and self-joins
  • Query optimization and EXPLAIN plans
  • Database-specific features (PostgreSQL's JSON operations, MySQL's full-text search)

Pick one. Learn it deeply. Build something with it. That's how you actually learn SQL.