SQL Overview
SQL (Structured Query Language) serves as the standard communication protocol for relational database systems. It provides a comprehensive command set specifically designed for database operations, enabling users to issue declarative instructions without worrying about implementation details.
SQL Syntax Standards
-
SQL keywords are case-insensitive, though uppercase is conventionally preferred. String literals, however, maintain their case sensitviity.
-
Statements can span single or multiple lines, terminated by a semicolon. Keywords cannot be abbreviated or split across lines.
-
Whitespace and indentation enhance readability. Placing clauses on separate lines improves maintainability.
SELECT * FROM users
WHERE username = "admin";
-
Comments use
--for single-line and/* */for multi-line blocks. -
Long statements can be broken across lines for better formatting.
-
SQL commands are categorized into DDL, DML, and DCL:
-- DML (Data Manipulation Language)
-- Handles data retrieval and modification: SELECT, UPDATE, INSERT, DELETE
-- DDL (Data Definition Language)
-- Manages object creation and structure: CREATE, ALTER, DROP
-- DCL (Data Control Language)
-- Controls access permissions: GRANT, DENY, REVOKE
Database-Level Operations
-- Create a new database
CREATE DATABASE IF NOT EXISTS company_db CHARACTER SET utf8mb4;
-- List all databases
SHOW DATABASES;
-- View database creation details
SHOW CREATE DATABASE company_db;
-- Modify database charset
ALTER DATABASE company_db CHARACTER SET utf8mb4;
-- Delete a database
DROP DATABASE IF EXISTS company_db;
-- Switch active database
USE company_db;
-- Identify current database
SELECT DATABASE();
Table Structure Management
Creating Tables
CREATE TABLE staff (
id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(50),
department VARCHAR(30),
hire_date DATE,
salary DECIMAL(10,2),
is_active TINYINT(1) DEFAULT 1
);
Key constraints include:
PRIMARY KEY: Ensures uniqueness and non-null valuesUNIQUE: Prevents duplicate valuesNOT NULL: Prohibits empty valuesAUTO_INCREMENT: Automatically generates sequential values for primary keys
Inspecting Table Metadata
DESC staff;
SHOW COLUMNS FROM staff;
SHOW TABLES;
SHOW CREATE TABLE staff;
Modifying Table Structure
-- Add new columns
ALTER TABLE staff ADD COLUMN phone VARCHAR(20) AFTER emp_name;
ALTER TABLE staff ADD COLUMN title VARCHAR(30) FIRST;
-- Modify column definitions
ALTER TABLE staff MODIFY salary DECIMAL(12,2) UNSIGNED;
ALTER TABLE staff MODIFY title VARCHAR(40) AFTER department;
-- Rename columns
ALTER TABLE staff CHANGE COLUMN title job_title VARCHAR(35);
-- Remove columns
ALTER TABLE staff DROP COLUMN phone;
-- Rename entire table
RENAME TABLE staff TO employees;
-- Change charset
ALTER TABLE employees CHARACTER SET utf8mb4;
Deleting Tables
DROP TABLE employees;
Data Manipulation Operations
Inserting Records
-- Single row insertion with column specification
INSERT INTO employees (emp_name, department, hire_date, salary)
VALUES ('john_smith', 'engineering', '2023-01-15', 8500.00);
-- Single row insertion without column specification
INSERT INTO employees VALUES
(NULL, 'jane_doe', 'marketing', '2022-11-20', 7200.00, 1);
-- Partial column insertion
INSERT INTO employees (emp_name, salary) VALUES
('new_hire', 5500.00);
-- Multiple rows at once
INSERT INTO employees (emp_name, department, salary) VALUES
('alice_wang', 'sales', 6800.00),
('bob_zhang', 'sales', 7100.00),
('carol_li', 'hr', 6400.00);
-- SET syntax for insertion
INSERT INTO employees SET emp_name = 'david_chen', salary = 9000.00;
Updating Records
UPDATE employees SET salary = salary * 1.05 WHERE department = 'sales';
UPDATE employees SET hire_date = '2023-06-01' WHERE emp_name = 'john_smith';
Deleting Records
-- Conditional deletion
DELETE FROM employees WHERE emp_name = 'inactive_user';
-- Remove all records (identity counter persists)
DELETE FROM employees;
ALTER TABLE employees AUTO_INCREMENT = 1;
-- Truncate clears all data and resets counter
TRUNCATE TABLE employees;
Note: After inserting a record with id=7, subsequent AUTO_INCREMENT values will continue from 7.
Query Operations
Basic Query Syntax
SELECT [DISTINCT] column_list FROM table_name
[WHERE conditions]
[GROUP BY grouping_column]
[HAVING group_conditions]
[ORDER BY sort_column [ASC|DESC]]
[LIMIT offset, row_count];
Sample Data Setup
CREATE TABLE exam_scores (
student_id INT PRIMARY KEY AUTO_INCREMENT,
student_name VARCHAR(20),
mathematics DECIMAL(5,2),
physics DECIMAL(5,2),
chemistry DECIMAL(5,2)
);
INSERT INTO exam_scores VALUES
(1, 'tom_huang', 92, 88, 95),
(2, 'lisa_wu', 78, 91, 83),
(3, 'mike_zhou', 65, 72, 68),
(4, 'sarah_lin', 88, 94, 91),
(5, 'kevin_ma', 73, 85, 77),
(6, 'emma_fan', 81, 96, 62);
Simple SELECT Queries
-- Retrieve all records
SELECT * FROM exam_scores;
-- Select specific columns
SELECT student_name, mathematics FROM exam_scores;
-- Eliminate duplicate results
SELECT DISTINCT mathematics FROM exam_scores;
-- Use computed expressions with aliases
SELECT student_name,
mathematics + physics + chemistry AS total_score
FROM exam_scores;
SELECT student_name,
mathematics + 10 AS adjusted_math
FROM exam_scores;
Filtering with WHERE Clause
-- Exact match
SELECT * FROM exam_scores WHERE student_name = 'tom_huang';
-- Numeric comparison
SELECT student_name, mathematics FROM exam_scores WHERE mathematics > 85;
-- Aggregate condition
SELECT student_name, total_score FROM exam_scores
WHERE (mathematics + physics + chemistry) > 260;
-- Range filtering
SELECT * FROM exam_scores WHERE physics BETWEEN 80 AND 95;
-- Membership testing
SELECT * FROM exam_scores WHERE mathematics IN (88, 92, 95);
-- Pattern matching with wildcards
SELECT * FROM exam_scores WHERE student_name LIKE 's%';
-- % matches any sequence, _ matches single character
-- Combining conditions
SELECT * FROM exam_scores
WHERE mathematics > 80 AND physics >= 90;
Sorting Results
-- Ascending sort (default)
SELECT * FROM exam_scores ORDER BY mathematics;
-- Descending sort
SELECT student_name,
(mathematics + physics + chemistry) AS total
FROM exam_scores ORDER BY total DESC;
-- Multiple sort criteria
SELECT * FROM exam_scores
WHERE student_name LIKE 'e%'
ORDER BY mathematics DESC, physics DESC;
Grouping with GROUP BY
CREATE TABLE product_orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(30),
unit_price DECIMAL(8,2),
order_date DATE,
category VARCHAR(20)
);
INSERT INTO product_orders (product_name, unit_price, order_date, category) VALUES
('laptop', 5500.00, '2024-03-15', 'electronics'),
('wireless_mouse', 89.00, '2024-03-15', 'electronics'),
('mechanical_keyboard', 299.00, '2024-03-15', 'electronics'),
('standing_desk', 1200.00, '2024-03-16', 'furniture'),
('monitor_stand', 150.00, '2024-03-16', 'furniture'),
('office_chair', 680.00, '2024-03-16', 'furniture'),
('desk_lamp', 45.00, '2024-03-17', 'furniture');
-- Group and aggregate
SELECT category, SUM(unit_price) AS category_total
FROM product_orders GROUP BY category;
-- Filter grouped results with HAVING
SELECT category, SUM(unit_price) AS total_revenue
FROM product_orders GROUP BY category
HAVING SUM(unit_price) > 500;
-- GROUP_CONCAT for grouped string aggregation
SELECT category, GROUP_CONCAT(product_name) AS items
FROM product_orders GROUP BY category;
HAVING vs WHERE: WHERE filters rows before aggregation; HAVING filters groups after aggregation. HAVING supports aggregate functions while WHERE does not.
Aggregate Functions
-- COUNT operations
SELECT COUNT(*) FROM exam_scores;
SELECT COUNT(physics) FROM exam_scores WHERE physics > 85;
SELECT COUNT(*) FROM exam_scores
WHERE (mathematics + physics + chemistry) > 250;
-- SUM operations
SELECT SUM(mathematics) AS math_total FROM exam_scores;
SELECT SUM(mathematics) AS math_sum,
SUM(physics) AS physics_sum,
SUM(chemistry) AS chemistry_sum
FROM exam_scores;
-- AVG operations
SELECT AVG(IFNULL(mathematics, 0)) AS avg_math FROM exam_scores;
SELECT AVG((mathematics + physics + chemistry)) AS avg_total
FROM exam_scores;
-- MAX and MIN operations
SELECT MAX((mathematics + physics + chemistry)) AS highest_score
FROM exam_scores;
SELECT MIN((mathematics + physics + chemistry)) AS lowest_score
FROM exam_scores;
-- Handle NULL values with IFNULL
SELECT MAX(IFNULL(mathematics, 0)) FROM exam_scores;
Limiting Results
-- First 3 records
SELECT * FROM exam_scores LIMIT 3;
-- Skip 2, then take 5
SELECT * FROM exam_scores LIMIT 2, 5;
Regular Expression Matching
SELECT * FROM employees WHERE emp_name REGEXP '^j';
SELECT * FROM employees WHERE emp_name REGEXP 'n$';
SELECT * FROM employees WHERE emp_name REGEXP 'a{2}';
Foreign Key Relationships
Creating Foreign Keys
-- Parent table: departments
CREATE TABLE departments (
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(30),
location VARCHAR(50)
);
INSERT INTO departments (dept_name, location) VALUES
('engineering', 'building_a'),
('marketing', 'building_b'),
('finance', 'building_c');
-- Child table: employees (with optional foreign key constraint)
CREATE TABLE staff_members (
staff_id INT PRIMARY KEY AUTO_INCREMENT,
staff_name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
) ENGINE=INNODB;
INSERT INTO staff_members (staff_name, dept_id) VALUES
('person_one', 1),
('person_two', 2),
('person_three', 1);
Adding and Removing Foreign Keys
ALTER TABLE staff_members
ADD CONSTRAINT fk_dept
FOREIGN KEY (dept_id) REFERENCES departments(dept_id);
ALTER TABLE staff_members DROP FOREIGN KEY fk_dept;
ON DELETE/UPDATE Actions
-- CASCADE: Delete/update child records when parent is deleted/updated
CREATE TABLE staff_members (
staff_id INT PRIMARY KEY AUTO_INCREMENT,
staff_name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE CASCADE
);
-- SET NULL: Set child foreign key to NULL when parent is deleted
CREATE TABLE staff_members (
staff_id INT PRIMARY KEY AUTO_INCREMENT,
staff_name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE SET NULL
);
-- RESTRICT: Prevent deletion/update of parent with children
-- NO ACTION: Similar to RESTRICT in MySQL
Multi-Table Queries
Preparation
CREATE TABLE company_employee (
emp_id INT AUTO_INCREMENT PRIMARY KEY,
emp_name VARCHAR(50),
age INT,
dept_id INT
);
INSERT INTO company_employee (emp_name, age, dept_id) VALUES
('worker_alpha', 25, 100),
('worker_beta', 32, 101),
('worker_gamma', 28, 101),
('worker_delta', 41, 102),
('worker_epsilon', 29, 100),
('worker_zeta', 35, 103);
CREATE TABLE company_department (
dept_id INT,
dept_name VARCHAR(100)
);
INSERT INTO company_department VALUES
(100, 'operations'),
(101, 'development'),
(102, 'support'),
(103, 'logistics');
Cross Join (Cartesian Product)
SELECT * FROM company_employee, company_department;
Inner Join
SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
INNER JOIN company_department d ON e.dept_id = d.dept_id;
-- Equivalent implicit join
SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e, company_department d
WHERE e.dept_id = d.dept_id;
Outer Joins
-- Left outer join: all from left table + matched from right
SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
LEFT JOIN company_department d ON e.dept_id = d.dept_id;
-- Right outer join: all from right table + matched from left
SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
RIGHT JOIN company_department d ON e.dept_id = d.dept_id;
-- Full outer join (emulated via UNION)
(SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
RIGHT JOIN company_department d ON e.dept_id = d.dept_id)
UNION
(SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
LEFT JOIN company_department d ON e.dept_id = d.dept_id);
-- UNION removes duplicates; UNION ALL preserves all rows
Compound Condition Joins
SELECT DISTINCT d.dept_name
FROM company_employee e
JOIN company_department d ON e.dept_id = d.dept_id
WHERE e.age >= 30;
SELECT e.emp_id, e.emp_name, e.age, d.dept_name
FROM company_employee e
JOIN company_department d ON e.dept_id = d.dept_id
ORDER BY e.age ASC;
Subqueries
-- IN subquery
SELECT * FROM company_employee
WHERE dept_id IN (SELECT dept_id FROM company_department);
-- Comparison operators in subquery
SELECT d.dept_id, d.dept_name
FROM company_department d
WHERE dept_id IN (
SELECT DISTINCT dept_id FROM company_employee WHERE age > 28
);
-- EXISTS subquery
SELECT * FROM company_employee e
WHERE EXISTS (
SELECT 1 FROM company_department d WHERE d.dept_id = 102
);
Note: When EXISTS returns TRUE, the outer query executes; when FALSE, results are empty.