SQL lessons guide

← Home page
📖 Lessons guide
🏠 Home 🎯 Comprehensive practice 📚 Study material ✏️ Exercises 🧠 quiz
Lesson 1 / 4
🗄️ Introduction to SQL and Databases
What SQL is, why it's needed, what a database looks like, and the difference between SQL and Excel.
SQL Database Primary Key Foreign Key Northwind
🔍 What is SQL?
definition
SQL = Structured Query Language — a built-in query language.

Unlike languages like Python or Java that are "general-purpose languages", SQL is A domain-specific language (Domain-Specific) which was created solely for working with data.

SQL's syntax resembles Simple English — commands like SELECT, FROM, WHERE make the code readable and understandable.
📜
History: SQL was developed in the 1970s at IBM labs and was originally called SEQUEL. Since then it has remained the leading language in the data world — for over 50 years!
❓ Why SQL at all instead of Excel?
3 key reasons
1. Big Data — massive data volumes
When Excel starts to "stutter" (around a million rows) — SQL is just getting warmed up. SQL processes billions of rows in a few seconds.

2. "The single source of truth" — a central database
Unlike files that circulate over email and get changed, SQL maintains a single central database, prevents mistakes and preserves valid logical relationships.

3. Surgical extraction
Excel loads the entire file into memory. SQL retrieves only the specific data you need — saves memory and shortens run times.
criterionExcelSQL
Number of rowsUp to ~1M rowsBillions of rows
speedSlow with large filesVery fast
Data sharingFiles floating aroundOne central database
securityAnyone can editPermissions and control
Performing calculationsConvenient for a regular userPowerful and precise
🗃️ What does a database look like?
Database structure
A database is made up ofTables (Tables). Each table contains Columns (Columns) FRows (Rows).

Imagine a bunch of Excel sheets linked to each other — that's what a database looks like.
1
Database — the overall database (e.g. Northwind)
2
Tables — tables inside the database (for example: customers, orders, products)
3
Columns — columns in a table (for example: id, first_name, city)
4
Rows — data rows (for example: a specific customer's details)
🔑 Keys and relationships
Primary Key — primary key
A column that identifies each row unique. There can't be two identical values. There can't be a NULL.
example: column id In the customers table — every customer has their own unique number.
Foreign Key — foreign key
A column in one table that points toPrimary Key in another table. Creates the relationship between the tables.
example: column customer_id in the orders table points toid In the customers table.
💡
tip: The relationship between a primary key and a foreign key is the foundation of the JOIN command — it is how you connect information from different tables.
🏪 The course database — Northwind
What is Northwind?
A classic database that simulates Trading company. Includes customers, orders, products, suppliers and employees. Widely used for learning SQL for decades.
The main tables
customers — Customers | orders — Orders | order_details — order details
products — Products | employees — Employees | suppliers — Suppliers | shippers — shipping companies
Lesson 2 / 4
📝 String functions (String Functions)
Cleaning, extraction, formatting and validation of text data. 4 main action categories.
UPPER / LOWER LEFT / RIGHT SUBSTRING TRIM / REPLACE LENGTH CONCAT
🧹 Category 1: data cleaning (Cleaning)
TRIM / LTRIM / RTRIM
Removes extra spaces from the string.
TRIM — from both sides | LTRIM — left side only | RTRIM — right side only
SQL
SELECT TRIM('   Hello World   ') AS clean_text;
-- result: 'Hello world'

SELECT company, TRIM(company) AS clean_company
FROM customers;
REPLACE
Replaces one substring with another.
REPLACE(column, 'what to replace', 'with what')
SQL
-- Removing dashes from phone numbers
SELECT REPLACE(business_phone, '-', '') AS clean_phone
FROM customers;

-- replacing UK with United Kingdom
SELECT REPLACE(country_region, 'UK', 'United Kingdom')
FROM customers;
✂️ Category 2: Data Extraction
LEFT / RIGHT
LEFT(column, N) — N characters from the start
RIGHT(column, N) — N characters from the end
SQL
-- first 3 letters of the company name
SELECT company, LEFT(company, 3) AS short_code
FROM customers;

-- last 4 digits of the phone
SELECT RIGHT(business_phone, 4) AS phone_end
FROM customers;
SUBSTRING
SUBSTRING(column, start, length) — extracting from a specific position.
important: The position starts at 1 (not 0!).
SQL
-- 5 characters starting from character 2
SELECT SUBSTRING(product_name, 2, 5) AS middle_text
FROM products;
LENGTH and INSTR
LENGTH(column) — returns the string's length in number of characters
INSTR(column, 'char') — returns the position of a substring
SQL
SELECT email_address,
       LENGTH(email_address) AS email_length,
       INSTR(email_address, '@') AS at_position
FROM employees;
🎨 Category 3: reformatting (Formatting)
CONCAT and CONCAT_WS
CONCAT(a, b, c) — concatenates strings. If one of the values is NULL — the result is NULL.
CONCAT_WS(separator, a, b, c) — joins with a fixed delimiter and skips NULL automatically.
SQL
-- full address with commas (skips NULL)
SELECT CONCAT_WS(', ', city, state_province, zip_postal_code) AS full_address
FROM customers;

-- price with a $ sign
SELECT CONCAT('$', ROUND(list_price, 2)) AS formatted_price
FROM products;
✅ Category 4: Validity and consistency (Standardization)
UPPER / LOWER
UPPER(column) — turns all the text into uppercase letters
LOWER(column) — turns all the text into lowercase letters

Useful for normalizing data before comparison.
SQL
SELECT UPPER(company) AS company_caps
FROM suppliers;

SELECT LOWER(email_address) AS standard_email
FROM employees;
⚠️
note: UPPER/LOWER change the Display only — they don't save the change to the table. For a permanent change you need UPDATE.
Lesson 3 / 4
🔎 WHERE — filtering data
Filtering rows by conditions. The most essential tool for real queries — includes AND/OR, IN, BETWEEN, LIKE, and IS NULL.
WHERE AND / OR IN BETWEEN LIKE IS NULL
📍 WHERE basics
syntax
WHERE arrives After FROM and filters rows by a condition.
Comparison operators: = != < > <= >=
SQL
-- products priced above 50
SELECT product_name, list_price
FROM products
WHERE list_price > 50;

-- Employees who are not Sales Representative
SELECT first_name, job_title
FROM employees
WHERE job_title != 'Sales Representative';
🔗 AND / OR — Multiple Conditions
the difference
AND — second The conditions must be met
OR — At least one Of the conditions must hold

Parentheses set precedence: WHERE (a OR b) AND c
SQL
-- AND: customers from USA and New York
SELECT * FROM customers
WHERE country_region = 'USA'
  AND city = 'New York';

-- OR: products from Beverages or Condiments
SELECT product_name FROM products
WHERE category = 'Beverages'
   OR category = 'Condiments';
📋 IN — list of values
What is IN?
IN It's a convenient shorthand for multiple OR conditions. More readable and shorter.
WHERE city IN ('Tel Aviv', 'Jerusalem', 'Haifa')
SQL
-- Suppliers from 3 cities (shorthand for OR)
SELECT company, city
FROM suppliers
WHERE city IN ('Tokyo', 'London', 'New York');

-- NOT IN: not from these cities
SELECT * FROM customers
WHERE country_region NOT IN ('USA', 'UK');
📏 BETWEEN — a range of values
Important to know
BETWEEN x AND y — includes both endpoints (x and y themselves).
equivalent to: WHERE col >= x AND col <= y
SQL
-- Orders with shipping fees between 20 and 50
SELECT id, shipping_fee
FROM orders
WHERE shipping_fee BETWEEN 20 AND 50;
🔍 LIKE — pattern search
Wildcard characters
% — any number of characters (even zero)
_ — exactly one character

'%gmail%' — contains gmail | 'j%' — starts with j | '%@gmail.com' — ends with @gmail.com
SQL
-- emails ending in northwindtraders.com
SELECT * FROM employees
WHERE email_address LIKE '%northwindtraders.com';

-- names that start with A
SELECT first_name FROM employees
WHERE first_name LIKE 'A%';
🚫 IS NULL / IS NOT NULL
NULL — a missing value
NULL is a value Missing / unknown — not zero, not empty!
Cannot use= NULL — must IS NULL.
SQL
-- orders not yet shipped
SELECT id, order_date
FROM orders
WHERE shipped_date IS NULL;

-- Customers with a website
SELECT company, web_page
FROM customers
WHERE web_page IS NOT NULL;
❌
Common error! WHERE shipped_date = NULL — this doesn't work in SQL! Always useIS NULL.
Lesson 4 / 4
📦 GROUP BY and aggregation
Grouping rows and computing statistics — COUNT, SUM, AVG, MIN, MAX. Includes HAVING, ROLLUP and UNION.
GROUP BY COUNT SUM / AVG MIN / MAX HAVING ROLLUP UNION
📊 Aggregate functions
The main functions
COUNT(*) — counts rows | COUNT(column) — counts non-NULL values
SUM(column) — computes a sum
AVG(column) — computes an average
MIN(column) — the lowest value
MAX(column) — the highest value
SQL
-- statistics on the products table
SELECT
  COUNT(*) AS total_products,
  AVG(list_price) AS avg_price,
  MIN(list_price) AS min_price,
  MAX(list_price) AS max_price
FROM products;
🗂️ GROUP BY — grouping into groups
The important rule
Every column in SELECT that isn't an aggregate function must to be in the GROUP BY.
GROUP BY splits the table into groups and computes an aggregate for each group.
SQL
-- how many products are in each category?
SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- average price per category
SELECT category, AVG(list_price) AS avg_price
FROM products
GROUP BY category
ORDER BY avg_price DESC;
🔍 HAVING — Filtering After Grouping
WHERE vs. HAVING
WHERE — filters rows before the grouping
HAVING — filters groups after the grouping

rule: You can't use aggregate functions (COUNT, SUM…) in WHERE. That's why you need HAVING.
SQL
-- Categories with more than 5 products
SELECT category, COUNT(*) AS cnt
FROM products
GROUP BY category
HAVING COUNT(*) > 5;

-- WHERE (before) + HAVING (after)
SELECT category, AVG(list_price) AS avg
FROM products
WHERE list_price > 10        -- filter before grouping
GROUP BY category
HAVING AVG(list_price) > 30; -- filter after grouping
💡
Trick to remember: WHERE → before GROUP BY (on rows). HAVING → after GROUP BY (on groups).
📈 ROLLUP — grand summary
What is ROLLUP?
GROUP BY column WITH ROLLUP — adds a grand total row at the end of the results.
The last row will contain NULL in the grouping column and a grand total.
SQL
-- count by category + total
SELECT
  COALESCE(category, '--- Total ---') AS category,
  COUNT(*) AS total
FROM products
GROUP BY category WITH ROLLUP;
🔀 UNION — combining results
UNION vs UNION ALL
UNION — combines the results of two queries and removes duplicates
UNION ALL — merges and keeps duplicates (faster)

condition: Both queries must return the same number of columns with compatible data types.
SQL
-- a combined list of cities from customers and suppliers
SELECT city, 'customer' AS type FROM customers
UNION
SELECT city, 'supplier' AS type FROM suppliers
ORDER BY city;
📋 SQL execution order — important to know!
1
FROM — which table to pull from
2
WHERE — filtering rows
3
GROUP BY — grouping into groups
4
HAVING — filtering groups
5
SELECT — choosing columns to display
6
ORDER BY — sorting the results
7
LIMIT — limiting the number of rows
💡
The order that we we write SQL differs from the order the DB promotion it. This is why you can't use an Alias from SELECT inside WHERE.