Home / Notebooks / Database
Database
beginner

SQL Fundamentals

Essential SQL concepts and queries for working with relational databases

March 10, 2024
Updated regularly

SQL Fundamentals

Quick reference guide for SQL (Structured Query Language) fundamentals.

What is SQL?

SQL is a standard language for managing and manipulating relational databases:

  • Query data from tables
  • Insert, update, delete records
  • Create and modify database structures
  • Control access to data
  • Basic SELECT Queries

    Retrieve data from tables:

    -- ========== Select All Columns ==========
    SELECT * FROM users;
    
    -- ========== Select Specific Columns ==========
    SELECT id, name, email FROM users;
    
    -- ========== Select with Alias ==========
    SELECT 
        name AS full_name,
        email AS email_address
    FROM users;
    
    -- ========== Select Distinct Values ==========
    SELECT DISTINCT country FROM users;
    
    -- ========== Select with Calculation ==========
    SELECT 
        product_name,
        price,
        price * 1.1 AS price_with_tax
    FROM products;
    

    WHERE Clause (Filtering)

    Filter records based on conditions:

    -- ========== Basic Filtering ==========
    SELECT * FROM users 
    WHERE country = 'Indonesia';
    
    -- ========== Numeric Comparison ==========
    SELECT * FROM products 
    WHERE price > 100;
    
    -- ========== Multiple Conditions (AND) ==========
    SELECT * FROM users 
    WHERE country = 'Indonesia' 
      AND age >= 18;
    
    -- ========== Multiple Conditions (OR) ==========
    SELECT * FROM users 
    WHERE country = 'Indonesia' 
       OR country = 'Singapore';
    
    -- ========== IN Operator ==========
    SELECT * FROM users 
    WHERE country IN ('Indonesia', 'Singapore', 'Malaysia');
    
    -- ========== BETWEEN Operator ==========
    SELECT * FROM products 
    WHERE price BETWEEN 50 AND 200;
    
    -- ========== LIKE Operator (Pattern Matching) ==========
    SELECT * FROM users 
    WHERE name LIKE 'John%';  -- Starts with 'John'
    
    SELECT * FROM users 
    WHERE email LIKE '%@gmail.com';  -- Ends with '@gmail.com'
    
    -- ========== IS NULL / IS NOT NULL ==========
    SELECT * FROM users 
    WHERE phone IS NULL;
    
    SELECT * FROM users 
    WHERE phone IS NOT NULL;
    

    ORDER BY (Sorting)

    Sort query results:

    -- ========== Ascending Order (Default) ==========
    SELECT * FROM products 
    ORDER BY price ASC;
    
    -- ========== Descending Order ==========
    SELECT * FROM products 
    ORDER BY price DESC;
    
    -- ========== Multiple Columns ==========
    SELECT * FROM users 
    ORDER BY country ASC, name ASC;
    
    -- ========== Order by Calculated Column ==========
    SELECT 
        product_name,
        price,
        stock,
        price * stock AS total_value
    FROM products
    ORDER BY total_value DESC;
    

    LIMIT / TOP

    Limit the number of results:

    -- ========== MySQL / PostgreSQL ==========
    SELECT * FROM users 
    LIMIT 10;
    
    -- ========== SQL Server ==========
    SELECT TOP 10 * FROM users;
    
    -- ========== With OFFSET (Pagination) ==========
    SELECT * FROM users 
    LIMIT 10 OFFSET 20;  -- Skip first 20, get next 10
    

    Aggregate Functions

    Perform calculations on data:

    -- ========== COUNT ==========
    SELECT COUNT(*) FROM users;
    SELECT COUNT(DISTINCT country) FROM users;
    
    -- ========== SUM ==========
    SELECT SUM(price) FROM products;
    
    -- ========== AVG ==========
    SELECT AVG(price) FROM products;
    
    -- ========== MIN / MAX ==========
    SELECT MIN(price) FROM products;
    SELECT MAX(price) FROM products;
    
    -- ========== Multiple Aggregates ==========
    SELECT 
        COUNT(*) AS total_products,
        AVG(price) AS avg_price,
        MIN(price) AS min_price,
        MAX(price) AS max_price,
        SUM(stock) AS total_stock
    FROM products;
    

    GROUP BY

    Group rows and apply aggregate functions:

    -- ========== Basic Grouping ==========
    SELECT country, COUNT(*) AS user_count
    FROM users
    GROUP BY country;
    
    -- ========== Multiple Columns ==========
    SELECT country, city, COUNT(*) AS user_count
    FROM users
    GROUP BY country, city;
    
    -- ========== With Aggregate Functions ==========
    SELECT 
        category,
        COUNT(*) AS product_count,
        AVG(price) AS avg_price,
        SUM(stock) AS total_stock
    FROM products
    GROUP BY category;
    
    -- ========== HAVING (Filter After Grouping) ==========
    SELECT 
        category,
        COUNT(*) AS product_count
    FROM products
    GROUP BY category
    HAVING COUNT(*) > 5;
    

    JOIN Operations

    Combine data from multiple tables:

    -- ========== INNER JOIN ==========
    -- Returns only matching rows from both tables
    SELECT 
        orders.id,
        users.name,
        orders.total
    FROM orders
    INNER JOIN users ON orders.user_id = users.id;
    
    -- ========== LEFT JOIN ==========
    -- Returns all rows from left table, matching rows from right
    SELECT 
        users.name,
        orders.id AS order_id,
        orders.total
    FROM users
    LEFT JOIN orders ON users.id = orders.user_id;
    
    -- ========== RIGHT JOIN ==========
    -- Returns all rows from right table, matching rows from left
    SELECT 
        users.name,
        orders.id AS order_id,
        orders.total
    FROM orders
    RIGHT JOIN users ON orders.user_id = users.id;
    
    -- ========== Multiple Joins ==========
    SELECT 
        orders.id,
        users.name,
        products.product_name,
        order_items.quantity,
        order_items.price
    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;
    

    INSERT Data

    Add new records to tables:

    -- ========== Insert Single Row ==========
    INSERT INTO users (name, email, country)
    VALUES ('Yudi Nugraha', 'yudi@example.com', 'Indonesia');
    
    -- ========== Insert Multiple Rows ==========
    INSERT INTO users (name, email, country)
    VALUES 
        ('Alice', 'alice@example.com', 'Singapore'),
        ('Bob', 'bob@example.com', 'Malaysia'),
        ('Charlie', 'charlie@example.com', 'Indonesia');
    
    -- ========== Insert from SELECT ==========
    INSERT INTO archived_users (name, email, country)
    SELECT name, email, country
    FROM users
    WHERE created_at < '2020-01-01';
    

    UPDATE Data

    Modify existing records:

    -- ========== Update Single Column ==========
    UPDATE users
    SET country = 'Indonesia'
    WHERE id = 1;
    
    -- ========== Update Multiple Columns ==========
    UPDATE users
    SET 
        email = 'newemail@example.com',
        updated_at = CURRENT_TIMESTAMP
    WHERE id = 1;
    
    -- ========== Update with Calculation ==========
    UPDATE products
    SET price = price * 1.1
    WHERE category = 'Electronics';
    
    -- ========== Update Multiple Rows ==========
    UPDATE users
    SET status = 'inactive'
    WHERE last_login < '2023-01-01';
    

    DELETE Data

    Remove records from tables:

    -- ========== Delete Specific Rows ==========
    DELETE FROM users
    WHERE id = 1;
    
    -- ========== Delete with Condition ==========
    DELETE FROM orders
    WHERE status = 'cancelled'
      AND created_at < '2023-01-01';
    
    -- ========== Delete All Rows (Use with Caution!) ==========
    DELETE FROM temp_table;
    
    -- ========== TRUNCATE (Faster, removes all rows) ==========
    TRUNCATE TABLE temp_table;
    

    CREATE TABLE

    Define database structures:

    -- ========== Basic Table ==========
    CREATE TABLE users (
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(100) NOT NULL,
        email VARCHAR(255) UNIQUE NOT NULL,
        age INT,
        country VARCHAR(50),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    
    -- ========== Table with Foreign Key ==========
    CREATE TABLE orders (
        id INT PRIMARY KEY AUTO_INCREMENT,
        user_id INT NOT NULL,
        total DECIMAL(10, 2) NOT NULL,
        status VARCHAR(20) DEFAULT 'pending',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (user_id) REFERENCES users(id)
    );
    
    -- ========== Table with Constraints ==========
    CREATE TABLE products (
        id INT PRIMARY KEY AUTO_INCREMENT,
        product_name VARCHAR(200) NOT NULL,
        price DECIMAL(10, 2) NOT NULL CHECK (price > 0),
        stock INT DEFAULT 0 CHECK (stock >= 0),
        category VARCHAR(50),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_category (category)
    );
    

    ALTER TABLE

    Modify existing table structures:

    -- ========== Add Column ==========
    ALTER TABLE users
    ADD COLUMN phone VARCHAR(20);
    
    -- ========== Modify Column ==========
    ALTER TABLE users
    MODIFY COLUMN name VARCHAR(150) NOT NULL;
    
    -- ========== Drop Column ==========
    ALTER TABLE users
    DROP COLUMN phone;
    
    -- ========== Add Index ==========
    ALTER TABLE users
    ADD INDEX idx_email (email);
    
    -- ========== Add Foreign Key ==========
    ALTER TABLE orders
    ADD FOREIGN KEY (user_id) REFERENCES users(id);
    

    Subqueries

    Use queries inside other queries:

    -- ========== Subquery in WHERE ==========
    SELECT * FROM products
    WHERE price > (SELECT AVG(price) FROM products);
    
    -- ========== Subquery in SELECT ==========
    SELECT 
        name,
        (SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS order_count
    FROM users;
    
    -- ========== Subquery with IN ==========
    SELECT * FROM products
    WHERE category IN (
        SELECT DISTINCT category 
        FROM products 
        WHERE price > 100
    );
    
    -- ========== Subquery in FROM ==========
    SELECT category, avg_price
    FROM (
        SELECT category, AVG(price) AS avg_price
        FROM products
        GROUP BY category
    ) AS category_avg
    WHERE avg_price > 50;
    

    Common Functions

    Useful SQL functions:

    -- ========== String Functions ==========
    SELECT UPPER(name) FROM users;
    SELECT LOWER(email) FROM users;
    SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
    SELECT SUBSTRING(name, 1, 3) FROM users;
    SELECT LENGTH(name) FROM users;
    
    -- ========== Date Functions ==========
    SELECT CURRENT_DATE;
    SELECT CURRENT_TIMESTAMP;
    SELECT DATE_FORMAT(created_at, '%Y-%m-%d') FROM orders;
    SELECT YEAR(created_at), MONTH(created_at) FROM orders;
    SELECT DATE_ADD(created_at, INTERVAL 7 DAY) FROM orders;
    SELECT DATEDIFF(CURRENT_DATE, created_at) AS days_ago FROM orders;
    
    -- ========== Numeric Functions ==========
    SELECT ROUND(price, 2) FROM products;
    SELECT CEILING(price) FROM products;
    SELECT FLOOR(price) FROM products;
    SELECT ABS(balance) FROM accounts;
    
    -- ========== Conditional Functions ==========
    SELECT 
        name,
        CASE 
            WHEN age < 18 THEN 'Minor'
            WHEN age < 65 THEN 'Adult'
            ELSE 'Senior'
        END AS age_group
    FROM users;
    
    SELECT 
        product_name,
        COALESCE(discount_price, price) AS final_price
    FROM products;
    

    Transactions

    Ensure data consistency:

    -- ========== Basic Transaction ==========
    START TRANSACTION;
    
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE id = 2;
    
    COMMIT;  -- Save changes
    
    -- ========== Transaction with Rollback ==========
    START TRANSACTION;
    
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    -- If error occurs
    ROLLBACK;  -- Undo all changes
    

    Indexes

    Improve query performance:

    -- ========== Create Index ==========
    CREATE INDEX idx_email ON users(email);
    
    -- ========== Composite Index ==========
    CREATE INDEX idx_country_city ON users(country, city);
    
    -- ========== Unique Index ==========
    CREATE UNIQUE INDEX idx_unique_email ON users(email);
    
    -- ========== Drop Index ==========
    DROP INDEX idx_email ON users;
    

    Tips

  • Always use WHERE clause with UPDATE and DELETE to avoid affecting all rows
  • Use indexes on columns frequently used in WHERE, JOIN, and ORDER BY
  • Use LIMIT to test queries before running on large datasets
  • Use EXPLAIN to analyze query performance
  • Always backup data before running destructive operations
  • Use transactions for operations that must succeed or fail together
  • Avoid SELECT \* in production code - specify columns explicitly
  • Resources

  • SQL Tutorial - W3Schools
  • PostgreSQL Documentation
  • MySQL Documentation
  • SQL Server Documentation
  • Topics

    SQLDatabaseDataBackend

    Found This Helpful?

    If you have questions or suggestions for improving these notes, I'd love to hear from you.