Chapter 01
What is SQL?
Welcome to your journey into the language of data. SQL is the universal key to unlocking databases.

The Language of Databases

SQL stands for Structured Query Language. It's a programming language specifically designed to manage, query, and manipulate data stored in relational databases.

Think of a database as a highly organized collection of spreadsheets, and SQL as the language you use to ask questions about them, add new data, or change existing data.

🎯 Why Learn SQL?

SQL is one of the most in-demand skills in tech. It's used by data analysts, developers, product managers, marketers, and virtually anyone who works with data.

πŸ“Š

Data Analysis

Extract insights from millions of rows in seconds

πŸ—οΈ

Backend Dev

Power applications with persistent data storage

πŸ€–

Machine Learning

Prepare and clean datasets for models

πŸ’Ό

Business Intel

Generate reports and dashboards from live data

How SQL Works

You write a query β€” a statement telling the database what you want. The database processes it and returns results.

πŸ“ You Write a Query
β†’
βš™οΈ Database Engine Processes
β†’
πŸ“‹ Results Returned
SQL
-- Your very first SQL query!
SELECT 'Hello, SQL World!' AS greeting;
πŸ’‘

Result: This returns a single row with a column called greeting containing the text "Hello, SQL World!"

Popular SQL Databases

🐘 PostgreSQL

Open source, feature-rich, enterprise-grade

🐬 MySQL

Most popular open-source DB, powers the web

πŸͺΆ SQLite

Lightweight, file-based, great for learning

🏒 SQL Server

Microsoft's enterprise database solution

πŸ“Œ

Good news: Core SQL syntax is the same across all databases. Learn it once, use it everywhere. Minor syntax differences exist, but the fundamentals are universal.

🧠 Quick Check

What does SQL stand for?

Structured Query Language
Simple Question Language
Standard Query Logic
System Query Language
Chapter 02
Tables, Rows & Columns
Understanding the building blocks of every relational database.

The Structure of a Database

A relational database organizes data into tables. Each table represents one type of entity (like users, products, or orders).

πŸ“ Anatomy of a Table

Columns define WHAT data is stored (name, age, email). Rows represent individual records (one user, one product). Each cell holds a single value.

πŸ“Š Example: employees table
employees
idPKnamedepartmentsalary
1AliceEngineering95000
2BobMarketing72000
3CarolEngineering105000
4DaveSales68000

Key Terminology

πŸ—‚οΈ Table

A collection of related data organized in rows and columns (like a spreadsheet tab)

πŸ“ Column (Field)

A vertical set of data of the same type β€” like "name" or "salary"

πŸ“ Row (Record)

A horizontal entry β€” one complete item (one employee, one product)

πŸ”‘ Primary Key (PK)

A unique identifier for each row β€” usually an ID number

Data Types

Every column has a data type that defines what kind of values it can hold:

TypeDescriptionExample
INTEGERWhole numbers42, -7, 1000
VARCHAR(n)Text up to n characters'Alice', 'Hello'
TEXTUnlimited length textLong descriptions
DECIMALExact decimal numbers99.95, 3.14
DATECalendar date'2025-01-15'
BOOLEANTrue/FalseTRUE, FALSE
TIMESTAMPDate + time'2025-01-15 14:30:00'
⚠️

Note: Text values in SQL are always wrapped in single quotes: 'like this'. Numbers don't need quotes. This is a common source of errors!

🧠 Quick Check

In a database table, what does a ROW represent?

A category of data (like "name")
A single record or entry
The entire database
A connection between tables
Chapter 03
Querying Data
The SELECT statement β€” the most important command in SQL.

The SELECT Statement

SELECT is how you retrieve data from a database. It's the command you'll use most often.

SQL β€” Basic Syntax
SELECT column1, column2, ...
FROM table_name;

Select All Columns

Use * (asterisk) to get every column:

SQL
-- Get everything from the employees table
SELECT * FROM employees;
πŸ“‹ Result
idnamedepartmentsalary
1AliceEngineering95000
2BobMarketing72000
3CarolEngineering105000
4DaveSales68000

Select Specific Columns

SQL
-- Only get names and salaries
SELECT name, salary FROM employees;

Aliases with AS

Rename columns in the output using AS:

SQL
SELECT
  name AS employee_name,
  salary AS annual_salary,
  salary / 12 AS monthly_salary
FROM employees;

DISTINCT β€” Remove Duplicates

SQL
-- Get unique department names only
SELECT DISTINCT department FROM employees;
πŸ’‘

Pro Tip: Avoid using SELECT * in production. Always specify the columns you need β€” it's faster and makes your intent clear.

Practice

Write a SELECT Query

Write a query to select only the name and department columns from the employees table.

Chapter 04
WHERE & Operators
Filter your data to get exactly what you need.

The WHERE Clause

WHERE filters rows based on conditions. Only rows that meet the condition are returned.

SQL
SELECT * FROM employees
WHERE department = 'Engineering';
πŸ“‹ Result β€” Only Engineers
idnamedepartmentsalary
1AliceEngineering95000
3CarolEngineering105000

Comparison Operators

OperatorMeaningExample
=Equal tosalary = 95000
!= or <>Not equal todepartment != 'Sales'
>Greater thansalary > 80000
<Less thansalary < 80000
>=Greater or equalsalary >= 95000
<=Less or equalsalary <= 72000

Logical Operators

AND β€” Both conditions must be true

SQL
SELECT * FROM employees
WHERE department = 'Engineering'
  AND salary > 100000;

OR β€” At least one condition must be true

SQL
SELECT * FROM employees
WHERE department = 'Sales'
  OR department = 'Marketing';

NOT β€” Negate a condition

SQL
SELECT * FROM employees
WHERE NOT department = 'Sales';

Special Operators

BETWEEN β€” Range check

SQL
SELECT * FROM employees
WHERE salary BETWEEN 70000 AND 100000;

IN β€” Match any in a list

SQL
SELECT * FROM employees
WHERE department IN ('Engineering', 'Marketing');

LIKE β€” Pattern matching

SQL
-- Names starting with 'A'
SELECT * FROM employees
WHERE name LIKE 'A%';

-- Names containing 'ol'
SELECT * FROM employees
WHERE name LIKE '%ol%';
πŸ“Œ

LIKE Wildcards: % matches zero or more characters. _ matches exactly one character. So '_ob' matches "Bob" but not "Jacob".

IS NULL β€” Check for missing values

SQL
-- Find employees with no department assigned
SELECT * FROM employees
WHERE department IS NULL;
🚫

Common Mistake: Never use = NULL. Always use IS NULL or IS NOT NULL. NULL is not a value β€” it's the absence of a value.

Practice

Filter Employees

Write a query to find all employees with a salary greater than 80000.

Chapter 05
Sorting & Limiting
Control the order and quantity of your results.

ORDER BY β€” Sort Results

By default, SQL doesn't guarantee any particular order. Use ORDER BY to sort.

SQL
-- Sort by salary, highest first
SELECT * FROM employees
ORDER BY salary DESC;

-- Sort by name alphabetically (default is ASC)
SELECT * FROM employees
ORDER BY name ASC;

⬆️ ASC vs DESC

ASC (ascending) = A→Z, 1→100, oldest→newest. This is the default.
DESC (descending) = Z→A, 100→1, newest→oldest.

Multi-Column Sorting

SQL
-- Sort by department first, then salary within each dept
SELECT * FROM employees
ORDER BY department ASC, salary DESC;

LIMIT β€” Restrict Row Count

SQL
-- Get the top 2 highest-paid employees
SELECT * FROM employees
ORDER BY salary DESC
LIMIT 2;

OFFSET β€” Skip Rows (Pagination)

SQL
-- Skip first 2, then get next 2 (page 2)
SELECT * FROM employees
ORDER BY id
LIMIT 2 OFFSET 2;
SELECT
β†’
FROM
β†’
WHERE
β†’
ORDER BY
β†’
LIMIT
πŸ’‘

Execution Order: SQL processes clauses in this order β€” FROM β†’ WHERE β†’ SELECT β†’ ORDER BY β†’ LIMIT. The visual above shows the logical flow.

Practice

Top Earner

Write a query to get the single highest-paid employee's name and salary.

Chapter 06
Aggregation
Summarize data with aggregate functions and GROUP BY.

Aggregate Functions

These functions compute a single value from a set of rows:

COUNT()

Number of rows

SUM()

Total of values

AVG()

Average value

MIN()

Smallest value

SQL
-- Count all employees
SELECT COUNT(*) AS total_employees FROM employees;

-- Average salary
SELECT AVG(salary) AS avg_salary FROM employees;

-- Highest salary
SELECT MAX(salary) AS top_salary FROM employees;

GROUP BY β€” Aggregate Per Category

GROUP BY splits rows into groups and applies the aggregate function to each group separately.

SQL
-- Average salary per department
SELECT
  department,
  COUNT(*) AS num_employees,
  AVG(salary) AS avg_salary,
  MAX(salary) AS max_salary
FROM employees
GROUP BY department;
πŸ“‹ Result
departmentnum_employeesavg_salarymax_salary
Engineering2100000105000
Marketing17200072000
Sales16800068000

HAVING β€” Filter Groups

WHERE filters rows BEFORE grouping. HAVING filters AFTER grouping.

SQL
-- Only departments with avg salary above 75000
SELECT
  department,
  AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 75000;
⚠️

WHERE vs HAVING: Use WHERE to filter individual rows. Use HAVING to filter aggregated groups. You can use both in the same query!

Full Execution Order

FROM
β†’
WHERE
β†’
GROUP BY
β†’
HAVING
β†’
SELECT
β†’
ORDER BY
Practice

Department Stats

Write a query to find the total salary cost per department. Use SUM() and GROUP BY.

Chapter 07
JOINs
The most powerful concept in SQL β€” combining data from multiple tables.

Why JOINs?

Real databases have many tables. A user's name is in one table, their orders in another. JOINs let you combine them based on a shared key.

πŸ“Š Two Related Tables
employees
idPKnamedept_idFK
1Alice10
2Bob20
3Carol10
departments
idPKdept_name
10Engineering
20Marketing
30Sales

INNER JOIN

Returns only rows that have a match in both tables.

SQL
SELECT
  e.name,
  d.dept_name
FROM employees e
INNER JOIN departments d
  ON e.dept_id = d.id;

Types of JOINs

A B
INNER JOIN

Only matching rows

A B
LEFT JOIN

All from A + matches from B

A B
RIGHT JOIN

All from B + matches from A

A B
FULL OUTER JOIN

All rows from both

LEFT JOIN Example

SQL
-- All departments, even those with no employees
SELECT
  d.dept_name,
  e.name
FROM departments d
LEFT JOIN employees e
  ON d.id = e.dept_id;
πŸ“‹ LEFT JOIN Result (Sales has no employees)
dept_namename
EngineeringAlice
EngineeringCarol
MarketingBob
SalesNULL
πŸ’‘

When to use which? Use INNER JOIN when you only want matches. Use LEFT JOIN when you want all records from the "left" table regardless of match. LEFT JOIN is the most commonly used after INNER JOIN.

Practice

Write a JOIN

Write an INNER JOIN to get employee names with their department names from the two tables above.

Chapter 08
Subqueries
Queries inside queries β€” unlocking advanced data retrieval.

What is a Subquery?

A subquery is a SELECT statement nested inside another query. It runs first, and its result is used by the outer query.

SQL β€” Subquery in WHERE
-- Employees earning above average
SELECT name, salary
FROM employees
WHERE salary > (
  SELECT AVG(salary) FROM employees
);

πŸ”„ How It Executes

1. The inner query runs first: SELECT AVG(salary) FROM employees β†’ returns 85000
2. The outer query becomes: SELECT ... WHERE salary > 85000
3. Only Alice (95000) and Carol (105000) are returned.

Subquery with IN

SQL
-- Employees in departments that have 'Eng' in the name
SELECT name
FROM employees
WHERE dept_id IN (
  SELECT id FROM departments
  WHERE dept_name LIKE '%Eng%'
);

Subquery in FROM (Derived Table)

SQL
-- Get departments with their employee count, then filter
SELECT *
FROM (
  SELECT department, COUNT(*) AS emp_count
  FROM employees
  GROUP BY department
) AS dept_stats
WHERE emp_count > 1;

EXISTS β€” Check for Existence

SQL
-- Departments that have at least one employee
SELECT dept_name
FROM departments d
WHERE EXISTS (
  SELECT 1 FROM employees e
  WHERE e.dept_id = d.id
);
πŸ“Œ

Subquery vs JOIN: Many subqueries can be rewritten as JOINs. JOINs are often more readable and performant. Use subqueries when the logic is cleaner that way (like comparing to an aggregate).

Chapter 09
Data Modification
INSERT new data, UPDATE existing data, DELETE what you don't need.

INSERT β€” Add New Rows

SQL
-- Insert a single row
INSERT INTO employees (name, department, salary)
VALUES ('Eve', 'Engineering', 98000);

-- Insert multiple rows
INSERT INTO employees (name, department, salary)
VALUES
  ('Frank', 'Sales', 71000),
  ('Grace', 'Marketing', 78000);

UPDATE β€” Modify Existing Rows

SQL
-- Give Alice a raise
UPDATE employees
SET salary = 105000
WHERE name = 'Alice';

-- 10% raise for all engineers
UPDATE employees
SET salary = salary * 1.10
WHERE department = 'Engineering';
🚨

DANGER: Always include a WHERE clause with UPDATE and DELETE! Without it, you'll modify or delete EVERY row in the table. This is one of the most common and costly SQL mistakes.

DELETE β€” Remove Rows

SQL
-- Delete a specific employee
DELETE FROM employees
WHERE name = 'Dave';

-- Delete all employees in Sales
DELETE FROM employees
WHERE department = 'Sales';

-- ⚠️ DANGER: This deletes EVERYTHING!
-- DELETE FROM employees;

πŸ›‘οΈ Safety Checklist

Before running UPDATE or DELETE in production:

1. First run a SELECT with the same WHERE clause to see what will be affected
2. Count the rows: is the number what you expect?
3. Use a transaction (BEGIN / COMMIT / ROLLBACK) for safety
4. Back up your data regularly

SQL β€” Safe Pattern
-- Step 1: Preview what will be affected
SELECT * FROM employees
WHERE department = 'Sales';

-- Step 2: If looks good, then update inside a transaction
BEGIN;
UPDATE employees
SET salary = salary * 1.05
WHERE department = 'Sales';
-- Check the results
SELECT * FROM employees WHERE department = 'Sales';
-- If good:
COMMIT;
-- If bad:
-- ROLLBACK;
Chapter 10
Schema Design
Create your own tables and define relationships between them.

CREATE TABLE

SQL
CREATE TABLE employees (
  id        INTEGER PRIMARY KEY,
  name      VARCHAR(100) NOT NULL,
  email     VARCHAR(255) UNIQUE,
  dept_id   INTEGER,
  salary    DECIMAL(10,2) DEFAULT 0,
  hired_at  DATE DEFAULT CURRENT_DATE
);

Constraints

ConstraintPurpose
PRIMARY KEYUnique identifier for each row. Cannot be NULL.
NOT NULLColumn must always have a value.
UNIQUENo two rows can have the same value in this column.
DEFAULTAuto-fill value if none is provided.
CHECKCustom validation rule.
FOREIGN KEYLinks to a primary key in another table.

Foreign Keys β€” Relationships

A foreign key creates a link between two tables, enforcing that values in one column must exist in another table.

SQL
CREATE TABLE departments (
  id        INTEGER PRIMARY KEY,
  dept_name VARCHAR(100) NOT NULL
);

CREATE TABLE employees (
  id        INTEGER PRIMARY KEY,
  name      VARCHAR(100) NOT NULL,
  dept_id   INTEGER,
  FOREIGN KEY (dept_id) REFERENCES departments(id)
);

ALTER TABLE β€” Modify Structure

SQL
-- Add a column
ALTER TABLE employees
ADD COLUMN phone VARCHAR(20);

-- Remove a column
ALTER TABLE employees
DROP COLUMN phone;

-- Drop (delete) an entire table
DROP TABLE employees;
⚠️

DROP TABLE permanently deletes the table and ALL its data. Always double-check before running this command!

Chapter 11
Advanced Topics
Views, Indexes, Window Functions, and CTEs β€” take your SQL to the next level.

Views β€” Saved Queries

A view is a virtual table defined by a query. It doesn't store data β€” it runs the query each time you access it.

SQL
-- Create a view for high earners
CREATE VIEW high_earners AS
SELECT name, salary, department
FROM employees
WHERE salary > 90000;

-- Now use it like a regular table
SELECT * FROM high_earners;

Indexes β€” Speed Up Queries

An index is like a book's index β€” it helps the database find rows faster without scanning every row.

SQL
-- Create an index on the department column
CREATE INDEX idx_department
ON employees(department);

-- Queries filtering by department are now faster
SELECT * FROM employees
WHERE department = 'Engineering';
πŸ“Œ

Index Trade-offs: Indexes speed up reads but slow down writes (INSERT, UPDATE, DELETE). Add indexes on columns you frequently filter or join on.

CTEs β€” Common Table Expressions

A WITH clause lets you define named temporary result sets, making complex queries much more readable.

SQL
WITH dept_stats AS (
  SELECT
    department,
    AVG(salary) AS avg_salary,
    COUNT(*) AS emp_count
  FROM employees
  GROUP BY department
)
SELECT *
FROM dept_stats
WHERE avg_salary > 80000
ORDER BY avg_salary DESC;

Window Functions

Perform calculations across rows related to the current row, without collapsing them into groups.

SQL
-- Rank employees by salary within their department
SELECT
  name,
  department,
  salary,
  RANK() OVER (
    PARTITION BY department
    ORDER BY salary DESC
  ) AS dept_rank
FROM employees;

πŸͺŸ Common Window Functions

ROW_NUMBER() β€” Unique sequential number for each row
RANK() β€” Rank with gaps for ties (1, 2, 2, 4)
DENSE_RANK() β€” Rank without gaps (1, 2, 2, 3)
SUM() OVER() β€” Running total
LAG() / LEAD() β€” Access previous/next row's value

Running Total Example

SQL
SELECT
  name,
  salary,
  SUM(salary) OVER (
    ORDER BY salary
  ) AS running_total
FROM employees;
Chapter 12
Final Project
Put everything together β€” design and query a complete database.

🎯 Project: Online Bookstore

Design a database for an online bookstore with authors, books, customers, and orders. Then write queries to answer business questions.

πŸ“ Schema Design

We need four tables: authors, books, customers, and orders. Think about the relationships between them.

SQL β€” Create the Schema
-- 1. Authors
CREATE TABLE authors (
  id     INTEGER PRIMARY KEY,
  name   VARCHAR(200) NOT NULL,
  country VARCHAR(100)
);

-- 2. Books
CREATE TABLE books (
  id         INTEGER PRIMARY KEY,
  title      VARCHAR(300) NOT NULL,
  author_id  INTEGER REFERENCES authors(id),
  price      DECIMAL(8,2),
  genre      VARCHAR(50),
  published  DATE
);

-- 3. Customers
CREATE TABLE customers (
  id     INTEGER PRIMARY KEY,
  name   VARCHAR(200) NOT NULL,
  email  VARCHAR(255) UNIQUE NOT NULL,
  city   VARCHAR(100)
);

-- 4. Orders
CREATE TABLE orders (
  id          INTEGER PRIMARY KEY,
  customer_id INTEGER REFERENCES customers(id),
  book_id     INTEGER REFERENCES books(id),
  quantity    INTEGER DEFAULT 1,
  order_date  DATE DEFAULT CURRENT_DATE
);
πŸ”— Entity Relationship Diagram
authors
id PK
name
country
←
books
id PK
title
author_id FK
price, genre
β†’
orders
id PK
customer_id FK
book_id FK
quantity, date
←
customers
id PK
name
email, city

πŸ“ Challenges

Try writing SQL for each of these business questions:

Challenge 1

Top Selling Books

Find the top 5 best-selling books by total quantity ordered. Show the book title and total quantity.

Challenge 2

Revenue Per Author

Calculate total revenue per author (price Γ— quantity for all their books). Show author name and total revenue.

Challenge 3

Customers Who Never Ordered

Find all customers who have never placed an order. (Hint: LEFT JOIN or NOT EXISTS)

Challenge 4

Advanced: Rank Books Per Genre

Use a window function to rank books by price within each genre. Show title, genre, price, and rank.

πŸŽ‰ Congratulations!

You've completed the SQL Mastery course! You now understand SELECT, WHERE, JOINs, aggregation, subqueries, data modification, schema design, views, indexes, CTEs, and window functions.

Next steps: Practice on LeetCode SQL, HackerRank SQL, or SQLZoo. Try installing PostgreSQL or SQLite locally and build your own projects!