πŸ”’

Sign In Required

This workbook requires a free KaliRange account.

Log In β†’Create Account
πŸ‘Ά
Practice online (no install needed): go to sqliteonline.com or sqlfiddle.com β€” type SQL directly in the browser and see results instantly. Or install SQLite: sudo apt install sqlite3

Chapter 1 β€” What is SQL?

SQL (Structured Query Language) is the language for talking to databases. Almost every website, app, and business uses a database to store data β€” and SQL is how you get data in and out.

-- SQL databases (flavours β€” syntax is very similar):
SQLite    β†’ lightweight, file-based, perfect for learning
MySQL     β†’ most popular for web apps
PostgreSQL→ powerful, open-source
SQL Server→ Microsoft
Oracle    β†’ enterprise

-- SQL can:
-- SELECT β†’ read data
-- INSERT β†’ add data
-- UPDATE β†’ change data
-- DELETE β†’ remove data
-- CREATE β†’ create tables/databases
-- DROP   β†’ delete tables/databases

Chapter 2 β€” Setting Up SQLite

# Install SQLite on Linux/Kali:
sudo apt install sqlite3 -y

# Open/create a database file:
sqlite3 mydb.db

# SQLite commands (start with dot):
.tables          β†’ list all tables
.schema          β†’ show table structure
.quit            β†’ exit
.help            β†’ show all commands
.headers on      β†’ show column names in output
.mode column     β†’ formatted output

-- SQL statements end with semicolon (;)
-- Comments start with -- (two dashes)

Chapter 3 β€” CREATE & DROP Tables

-- Create a table for storing users
CREATE TABLE users (
    id      INTEGER PRIMARY KEY AUTOINCREMENT,
    name    TEXT    NOT NULL,
    email   TEXT    UNIQUE NOT NULL,
    age     INTEGER,
    created TEXT    DEFAULT (datetime('now'))
);

-- Column constraints:
-- PRIMARY KEY   β†’ unique identifier for each row
-- AUTOINCREMENT β†’ number increases automatically (1,2,3...)
-- NOT NULL      β†’ value required (can't be empty)
-- UNIQUE        β†’ no duplicate values allowed
-- DEFAULT       β†’ value if none provided

-- Common data types:
INTEGER   β†’ whole numbers (1, 42, -5)
REAL      β†’ decimal numbers (3.14, 9.99)
TEXT      β†’ text strings ("Alice", "hello@email.com")
BLOB      β†’ binary data (files, images)
NULL      β†’ no value

-- Delete a table (careful β€” no undo!):
DROP TABLE users;

-- Delete only if it exists (no error if missing):
DROP TABLE IF EXISTS users;

-- View table structure:
.schema users

Chapter 4 β€” INSERT Data

-- Insert one row (specify all columns):
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@email.com', 25);

-- Insert multiple rows at once:
INSERT INTO users (name, email, age) VALUES
    ('Bob',     'bob@email.com',   30),
    ('Charlie', 'charlie@email.com', 22),
    ('Diana',   'diana@email.com',  28),
    ('Eve',     'eve@email.com',    35);

-- If you insert all columns in order, you can skip the column list:
INSERT INTO users VALUES (NULL, 'Frank', 'frank@email.com', 27, datetime('now'));
-- NULL for id β†’ AUTOINCREMENT fills it in

-- Insert from another table:
INSERT INTO users_backup SELECT * FROM users;

Chapter 5 β€” SELECT Queries

-- Get all rows, all columns:
SELECT * FROM users;

-- Get specific columns only:
SELECT name, email FROM users;
SELECT name, age FROM users;

-- Rename columns in output (alias):
SELECT name AS "Full Name", age AS "Years Old" FROM users;

-- Get distinct (unique) values:
SELECT DISTINCT age FROM users;

-- Count rows:
SELECT COUNT(*) FROM users;
SELECT COUNT(*) AS total_users FROM users;

-- Basic calculations:
SELECT name, age, age + 10 AS "Age in 10 years" FROM users;

Chapter 6 β€” WHERE Filtering

-- Filter rows with WHERE:
SELECT * FROM users WHERE age = 25;
SELECT * FROM users WHERE name = 'Alice';

-- Comparison operators:
SELECT * FROM users WHERE age > 25;
SELECT * FROM users WHERE age >= 25;
SELECT * FROM users WHERE age < 30;
SELECT * FROM users WHERE age != 25;
SELECT * FROM users WHERE age BETWEEN 20 AND 30;

-- Combine conditions:
SELECT * FROM users WHERE age > 20 AND age < 30;
SELECT * FROM users WHERE name = 'Alice' OR name = 'Bob';

-- Check for NULL:
SELECT * FROM users WHERE age IS NULL;
SELECT * FROM users WHERE age IS NOT NULL;

-- IN β€” match multiple values:
SELECT * FROM users WHERE name IN ('Alice', 'Bob', 'Charlie');

-- LIKE β€” pattern matching:
SELECT * FROM users WHERE email LIKE '%gmail.com';    -- ends with gmail.com
SELECT * FROM users WHERE name LIKE 'A%';            -- starts with A
SELECT * FROM users WHERE name LIKE '%li%';          -- contains "li"
-- % = wildcard (any characters)
-- _ = single character wildcard

-- NOT β€” reverse a condition:
SELECT * FROM users WHERE NOT age = 25;
SELECT * FROM users WHERE name NOT LIKE 'A%';

Chapter 7 β€” UPDATE & DELETE

-- Update one row (ALWAYS use WHERE or you update ALL rows!):
UPDATE users SET age = 26 WHERE name = 'Alice';

-- Update multiple columns:
UPDATE users
SET age = 26, email = 'newalice@email.com'
WHERE id = 1;

-- Update all rows (be careful!):
UPDATE users SET age = age + 1;   -- everyone gets +1 year

-- Delete specific rows (ALWAYS use WHERE!):
DELETE FROM users WHERE name = 'Bob';
DELETE FROM users WHERE age < 18;

-- Delete ALL rows (dangerous!):
DELETE FROM users;

-- Best practice: always SELECT first to verify before UPDATE/DELETE:
SELECT * FROM users WHERE name = 'Bob';   -- check what will be deleted
DELETE FROM users WHERE name = 'Bob';     -- then delete

Chapter 8 β€” ORDER BY & LIMIT

-- Sort results:
SELECT * FROM users ORDER BY name;            -- A β†’ Z
SELECT * FROM users ORDER BY name ASC;        -- same as above
SELECT * FROM users ORDER BY name DESC;       -- Z β†’ A
SELECT * FROM users ORDER BY age DESC;        -- oldest first
SELECT * FROM users ORDER BY age ASC;         -- youngest first

-- Sort by multiple columns:
SELECT * FROM users ORDER BY age DESC, name ASC;
-- (sort by age descending, then by name alphabetically for same age)

-- Limit results:
SELECT * FROM users LIMIT 5;               -- first 5 rows only
SELECT * FROM users LIMIT 5 OFFSET 10;    -- rows 11-15 (for pagination)

-- Top 3 oldest users:
SELECT name, age FROM users ORDER BY age DESC LIMIT 3;

Chapter 9 β€” Aggregate Functions

-- Aggregate functions calculate values from multiple rows
SELECT COUNT(*) FROM users;          -- total number of rows
SELECT COUNT(age) FROM users;        -- rows where age is NOT NULL
SELECT SUM(age) FROM users;          -- sum of all ages
SELECT AVG(age) FROM users;          -- average age
SELECT MIN(age) FROM users;          -- youngest
SELECT MAX(age) FROM users;          -- oldest

-- Multiple aggregates at once:
SELECT
    COUNT(*) AS total,
    AVG(age) AS average_age,
    MIN(age) AS youngest,
    MAX(age) AS oldest
FROM users;

-- String functions:
SELECT UPPER(name) FROM users;       -- ALICE, BOB...
SELECT LOWER(email) FROM users;      -- lowercase emails
SELECT LENGTH(name) FROM users;      -- character count
SELECT SUBSTR(name, 1, 3) FROM users; -- first 3 chars

Chapter 10 β€” GROUP BY & HAVING

-- GROUP BY groups rows with same value and lets you aggregate each group

-- Count users by age:
SELECT age, COUNT(*) AS count
FROM users
GROUP BY age;

-- Average salary by department (if we had a salary column):
SELECT department, AVG(salary) AS avg_salary, COUNT(*) AS headcount
FROM employees
GROUP BY department;

-- HAVING filters groups (like WHERE but after GROUP BY):
SELECT age, COUNT(*) AS count
FROM users
GROUP BY age
HAVING count > 1;    -- only show ages with more than 1 person

-- WHERE vs HAVING:
-- WHERE filters ROWS (before grouping)
-- HAVING filters GROUPS (after grouping)

SELECT age, COUNT(*) AS count
FROM users
WHERE age > 18         -- filter rows first
GROUP BY age
HAVING count > 1;      -- then filter groups

Chapter 11 β€” JOINs

-- Create tables to practice JOINs:
CREATE TABLE orders (
    id         INTEGER PRIMARY KEY,
    user_id    INTEGER,
    product    TEXT,
    amount     REAL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

INSERT INTO orders (user_id, product, amount) VALUES
    (1, 'Laptop',  999.99),
    (1, 'Mouse',    29.99),
    (2, 'Keyboard', 79.99),
    (4, 'Monitor', 349.99);

-- INNER JOIN β€” only rows that match in BOTH tables:
SELECT users.name, orders.product, orders.amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- Only users who have placed orders

-- LEFT JOIN β€” ALL rows from left table, matching from right:
SELECT users.name, orders.product
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
-- ALL users, orders are NULL for those with no orders

-- Aliases for cleaner queries:
SELECT u.name, o.product, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.amount > 100
ORDER BY o.amount DESC;

-- Join summary:
INNER JOIN β†’ only matching rows (intersection)
LEFT JOIN  β†’ all left + matching right (NULLs for no match)
RIGHT JOIN β†’ all right + matching left (not in SQLite)
FULL JOIN  β†’ everything from both tables

Chapter 12 β€” Real Project: Student Database

-- Build a student grade tracking system

CREATE TABLE students (
    id      INTEGER PRIMARY KEY AUTOINCREMENT,
    name    TEXT NOT NULL,
    email   TEXT UNIQUE,
    grade   TEXT DEFAULT 'Active'
);

CREATE TABLE courses (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    code TEXT UNIQUE
);

CREATE TABLE enrollments (
    student_id INTEGER,
    course_id  INTEGER,
    score      REAL,
    FOREIGN KEY (student_id) REFERENCES students(id),
    FOREIGN KEY (course_id)  REFERENCES courses(id)
);

-- Add data:
INSERT INTO students (name, email) VALUES
    ('Uzair', 'uzair@example.com'),
    ('Sara',  'sara@example.com'),
    ('Ahmed', 'ahmed@example.com');

INSERT INTO courses (name, code) VALUES
    ('Python Programming', 'PY101'),
    ('Cybersecurity',      'CS201'),
    ('Networking',         'NET301');

INSERT INTO enrollments VALUES
    (1, 1, 95), (1, 2, 88),
    (2, 1, 72), (2, 3, 90),
    (3, 2, 85), (3, 3, 78);

-- Query: each student's average score:
SELECT s.name, ROUND(AVG(e.score), 2) AS average
FROM students s
JOIN enrollments e ON s.id = e.student_id
GROUP BY s.id
ORDER BY average DESC;

-- Query: best student per course:
SELECT c.name AS course, s.name AS top_student, MAX(e.score) AS score
FROM enrollments e
JOIN students s ON s.id = e.student_id
JOIN courses  c ON c.id = e.course_id
GROUP BY c.id;
βœ…
Workbook Complete! SQL is one of the most in-demand skills in tech. Practise on sqliteonline.com, then explore real datasets on kaggle.com. Also study SQL injection (see the security labs) to understand what bad SQL looks like.
Related: SQL Injection Lab β†’ ← All Workbooks
πŸ“–
Sign in to track progress