SQL with MySQL
1) Set Up Tools (with real-world example)
Before writing any SQL, we need a place where our database lives and a way to talk to it. Think
of it like this:
• Database = a school building where all data (students, teachers, marks) live.
• DBMS software (MySQL) = the school management system that organizes
everything inside the school.
• Workbench/Command line = the door and control room to go inside and talk to
the school.
Step 1. Install MySQL
• Download MySQL Community Server (8.0+) from MySQL’s official website.
• Install it. During installation, it will ask you to set a root password → remember this, it’s
like the master key to the school.
Real-world link: Imagine the root password is like the Principal’s key that unlocks every
classroom.
Step 2. Install a Tool to Talk to MySQL
You have 2 options:
1. MySQL Workbench (GUI) – a friendly app with windows, buttons, query editor.
o You can click, draw tables, and run queries.
2. Command Line Client – looks like a black terminal window.
o Type commands like:
o mysql -u root -p
(then enter your password).
Real-world link:
• Workbench = like using a computer in the school office to manage everything with
screens and charts.
• Command line = like talking directly with the principal using secret codes.
Step 3. Create Your First Playground Database
Open Workbench or the command line, then type:
-- Create a database (your new school building)
CREATE DATABASE schooldb;
-- Enter inside it (like going into the school building)
USE schooldb;
Congratulations! You just built your first school database. Right now it’s empty (like a new
building with no classrooms yet). Soon, we’ll add tables (classrooms) and data (students,
teachers, marks).
Practice Task:
• Install MySQL + Workbench.
• Open Workbench, connect with your root password.
• Run the above 2 commands.
• Check that it worked:
• SHOW DATABASES;
You should see schooldb in the list.
2) Core DB Terms (with real-world example)
DBMS (Database Management System)
• Software that stores and manages data.
• Example: MySQL is like a school’s filing system that keeps track of students, teachers,
subjects, marks.
Entity
• A real-world object we want to store data about.
• Example: In a school: Student, Teacher, Class, Subject are entities.
Attribute (Column)
• A property of an entity.
• Example: A Student has attributes: name, age, roll number, date of birth.
• Think of it like headings of columns in your school register.
Tuple (Row / Record)
• A single entry in a table.
• Example: One student’s information (Roll No. 5, Name = Aarav, Age = 11).
• Like one line in the school attendance register.
Domain
• The set of allowed values for a column.
• Example: Age of a student must be between 5 and 18.
• Like the rules that say “only students between grade 1 and 12 can study in school.”
Degree
• The number of columns in a table.
• Example: If Student table has 4 columns (roll, name, class, age), then degree = 4.
Cardinality
• Number of rows in a table (OR the type of relationship).
• Example (rows): If there are 50 students, the cardinality = 50.
• Example (relationship):
o One-to-One: Principal ↔ Office
o One-to-Many: Teacher ↔ Students
o Many-to-Many: Students ↔ Subjects
Keys
Keys are rules to uniquely identify or connect rows in tables.
• Primary Key (PK): Unique ID for each row.
Example: Student Roll Number.
• Foreign Key (FK): Connects tables.
Example: Student table has class_id which points to Class table.
• Candidate Key: Columns that could become a primary key.
Example: Roll Number, or maybe Admission Number.
• Alternate Key: A candidate key not chosen as primary.
Example: Admission Number if Roll Number is primary.
• Composite Key: Combination of 2+ columns.
Example: (Student ID + Subject ID) uniquely identifies an enrollment.
• Super Key: Any column set that can identify rows, even with extras.
Example: (Roll Number + Name) is also unique, though Roll Number alone is enough.
Constraints (Rules)
They protect data quality.
• NOT NULL: Column cannot be empty.
Example: A student must have a name.
• UNIQUE: No duplicates allowed.
Example: Every student must have a unique roll number.
• PRIMARY KEY: Combines NOT NULL + UNIQUE.
Example: Roll Number.
• FOREIGN KEY: Links tables.
Example: class_id in Students table → class_id in Classes table.
• CHECK: Enforces a condition.
Example: Marks must be between 0 and 100.
• DEFAULT: If no value is given, use a default.
Example: If room not mentioned, assign “TBD.”
• AUTO_INCREMENT: Automatically gives numbers.
Example: Student IDs go 1, 2, 3… automatically.
Practice Task:
1. Think of your classroom. Try to write down:
o Entity = Student
o Attributes = Roll No, Name, Age, Marks
o Tuple = Your own row
o Domain = Age between 10 and 12
o Degree = Number of columns you listed
o Cardinality = Number of students in your class
3) Create Tables (DDL)
Main DDL Commands
1. CREATE → make new database objects (tables, views, indexes).
2. ALTER → change structure of an existing table.
3. DROP → delete table (remove structure + data).
4. TRUNCATE → empty a table (delete all rows, keep structure).
5. RENAME → rename a table.
3.1 CREATE TABLE
Syntax:
CREATE TABLE table_name (
column_name datatype constraints,
...
);
Real-world Example (School Students Table):
CREATE TABLE students (
student_id INT PRIMARY KEY AUTO_INCREMENT, -- unique roll no
full_name VARCHAR(50) NOT NULL, -- student name
age INT CHECK (age BETWEEN 5 AND 18), -- domain: 5–18 yrs
class VARCHAR(10) NOT NULL,
admission_date DATE DEFAULT (CURDATE()) -- default = today
);
Think of this as creating a register with specific columns.
3.2 ALTER TABLE
Syntax:
ALTER TABLE table_name ADD column_name datatype;
ALTER TABLE table_name MODIFY column_name new_datatype;
ALTER TABLE table_name DROP COLUMN column_name;
ALTER TABLE table_name RENAME TO new_table_name;
Real-world Example:
-- Add a new column for phone number
ALTER TABLE students ADD phone VARCHAR(15);
-- Change column type: name can be longer
ALTER TABLE students MODIFY full_name VARCHAR(80);
-- Remove column phone
ALTER TABLE students DROP COLUMN phone;
-- Rename table students → pupils
ALTER TABLE students RENAME TO pupils;
More ALTER TABLE Commands in MySQL
1. Add Multiple Columns at Once
ALTER TABLE students
ADD (email VARCHAR(80), address VARCHAR(100));
Example: Adding both email and address for students in one go.
Syntax
ALTER TABLE table_name
ADD new_column_name datatype [constraints] AFTER existing_column_name;
Add Column after specific Column
ALTER TABLE students
ADD age INT AFTER full_name;
Add as the First Column
If you want to add at the beginning:
ALTER TABLE students
ADD admission_date DATE FIRST;
2. Change Column Name
ALTER TABLE students
CHANGE COLUMN full_name student_name VARCHAR(60) NOT NULL;
Example: Rename full_name → student_name and also change datatype/constraint.
3. Rename Only Column (short form in MySQL 8+)
ALTER TABLE students
RENAME COLUMN student_name TO full_name;
Example: Change student_name → full_name without touching datatype.
4. Rename Table (alternative way)
ALTER TABLE students RENAME TO pupils;
Example: Rename whole table to pupils.
5. Add Constraint (Keys, Checks)
-- Add unique constraint
ALTER TABLE students
ADD CONSTRAINT uq_email UNIQUE (email);
-- Add check constraint
ALTER TABLE students
ADD CONSTRAINT chk_age CHECK (age BETWEEN 5 AND 18);
-- Add foreign key
ALTER TABLE students
ADD CONSTRAINT fk_class FOREIGN KEY (class_id)
REFERENCES classes(class_id);
Example: Make sure email is unique, age is 5–18, and connect students → classes.
6. Drop Constraint
ALTER TABLE students DROP PRIMARY KEY;
ALTER TABLE students DROP FOREIGN KEY fk_class;
ALTER TABLE students DROP INDEX uq_email; -- UNIQUE = an index
ALTER TABLE students DROP CHECK chk_age;
Example: Removing a key/constraint if not needed.
7. Add / Drop Primary Key
-- Add
ALTER TABLE students
ADD PRIMARY KEY (student_id);
-- Drop
ALTER TABLE students
DROP PRIMARY KEY;
Example: Adding or removing a roll number as the unique ID.
8. Add / Drop Foreign Key
ALTER TABLE marks
ADD CONSTRAINT fk_marks_student FOREIGN KEY (student_id)
REFERENCES students(student_id);
ALTER TABLE marks DROP FOREIGN KEY fk_marks_student;
Example: Linking marks → students (or removing the link).
9. Change Storage Engine
ALTER TABLE students ENGINE = InnoDB;
Example: Switch from MyISAM → InnoDB to allow foreign keys.
10. Change Table Options
ALTER TABLE students AUTO_INCREMENT = 1000;
Example: Next student ID will start from 1000 instead of 1.
11. Reorder Columns
ALTER TABLE students
MODIFY phone VARCHAR(15) AFTER full_name;
ALTER TABLE students
MODIFY admission_date DATE FIRST;
Example: Move phone right after name, or put admission_date as first column.
12. Add / Drop Index
ALTER TABLE students ADD INDEX idx_name (full_name);
ALTER TABLE students DROP INDEX idx_name;
Example: Create/destroy an index for faster searching by name.
13. Disable / Enable Keys (bulk load optimization)
ALTER TABLE students DISABLE KEYS;
-- (insert many rows fast here)
ALTER TABLE students ENABLE KEYS;
Example: Pause updating indexes while adding 1000 students at once → faster inserts.
Practice Challenge:
Try these on your students table:
1. Rename age → student_age.
2. Add a unique constraint on phone.
3. Move phone column to be the first column.
4. Change AUTO_INCREMENT so the next student_id starts at 500.
5. Drop the unique constraint on phone.
3.3 DROP TABLE
Syntax:
DROP TABLE table_name;
Real-world Example:
DROP TABLE pupils;
Like throwing away the entire register (data + columns).
All DROP Commands in MySQL
1. Drop a Database
Syntax:
DROP DATABASE database_name;
Example:
DROP DATABASE schooldb;
Removes the whole school (all tables inside).
2. Drop a Table
Syntax:
DROP TABLE table_name;
Example:
DROP TABLE students;
Removes the students register (structure + data).
Drop multiple tables:
DROP TABLE students, teachers, classes;
3. Drop a Column
Syntax:
ALTER TABLE table_name DROP COLUMN column_name;
Example:
ALTER TABLE students DROP COLUMN phone;
Removes the phone column from student register.
4. Drop a Primary Key
Syntax:
ALTER TABLE table_name DROP PRIMARY KEY;
Example:
ALTER TABLE students DROP PRIMARY KEY;
Removes the unique roll number rule.
5. Drop a Foreign Key
Syntax:
ALTER TABLE table_name DROP FOREIGN KEY fk_name;
Example:
ALTER TABLE marks DROP FOREIGN KEY fk_marks_student;
Removes the link between marks → students.
6. Drop an Index (including UNIQUE)
Syntax:
ALTER TABLE table_name DROP INDEX index_name;
Example:
ALTER TABLE students DROP INDEX uq_email;
Removes the unique rule or search helper on email.
7. Drop a Check Constraint (MySQL 8+)
Syntax:
ALTER TABLE table_name DROP CHECK check_name;
Example:
ALTER TABLE students DROP CHECK chk_age;
Removes the rule like age must be between 5–18.
8. Drop a Default Value
Syntax:
ALTER TABLE table_name ALTER COLUMN column_name DROP DEFAULT;
Example:
ALTER TABLE students ALTER admission_date DROP DEFAULT;
Removes the default (like today’s date for admission).
9. Drop a View
Syntax:
DROP VIEW view_name;
Example:
DROP VIEW v_student_avg;
Deletes the saved query (view).
10. Drop a Procedure
Syntax:
DROP PROCEDURE procedure_name;
Example:
DROP PROCEDURE AddStudent;
11. Drop a Function
Syntax:
DROP FUNCTION function_name;
Example:
DROP FUNCTION GradeFromScore;
12. Drop a Trigger
Syntax:
DROP TRIGGER trigger_name;
Example:
DROP TRIGGER trg_marks_before_ins;
13. Drop an Event (if EVENT Scheduler is used)
Syntax:
DROP EVENT event_name;
Example:
DROP EVENT daily_cleanup;
Practice Task:
1. Create a table teachers.
2. Add a column phone.
3. Drop the column phone.
4. Drop the whole teachers table.
5. Finally, drop the database schooldb.
3.4 TRUNCATE TABLE
Syntax:
TRUNCATE TABLE table_name;
Real-world Example:
TRUNCATE TABLE students;
Clears all rows (empties the register) but keeps the structure, ready for new entries.
Remember:
• TRUNCATE is a DDL command (not DML).
• It removes all rows from a table but keeps the structure (so you can still insert new
rows).
• It resets AUTO_INCREMENT counters back to 1 (unless overridden).
• It is faster than DELETE because it doesn’t log row-by-row deletions.
TRUNCATE Syntax in MySQL
1. Truncate a Table
TRUNCATE TABLE table_name;
Example:
TRUNCATE TABLE students;
Removes all student records but keeps the students table ready for new data.
2. Truncate with Database Name
TRUNCATE TABLE database_name.table_name;
Example:
TRUNCATE TABLE schooldb.students;
Useful if you are working across multiple databases.
3. Truncate Temporary Tables
TRUNCATE TABLE temp_students;
Deletes all rows in a temporary table but structure remains.
4. Truncate with AUTO_INCREMENT Reset
By default, TRUNCATE resets the AUTO_INCREMENT counter to 1.
Example:
CREATE TABLE teachers (
teacher_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(50)
);
INSERT INTO teachers (full_name) VALUES ('Mr. Ali'), ('Ms. Sara');
TRUNCATE TABLE teachers;
INSERT INTO teachers (full_name) VALUES ('Mr. Khan');
teacher_id will start again from 1 (not 3).
5. Truncate with Foreign Keys
MySQL won’t let you truncate a table if it’s referenced by a foreign key constraint.
You must disable foreign key checks first:
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE marks;
SET FOREIGN_KEY_CHECKS = 1;
Example: If marks table references students, you must temporarily disable checks.
Important Notes About TRUNCATE
• You cannot use WHERE with TRUNCATE (use DELETE instead).
• You cannot truncate multiple tables in one statement.
• Truncate is usually faster than DELETE for clearing entire tables.
Practice Task:
1. Create a table library_books with an AUTO_INCREMENT book_id.
2. Insert 5 books.
3. TRUNCATE the table and insert 2 more books.
Notice how the book IDs start again from 1.
3.5 RENAME TABLE
Syntax:
RENAME TABLE old_name TO new_name;
Real-world Example:
RENAME TABLE students TO class5_students;
Like pasting a new label on the register.
RENAME is used for changing names of tables, databases, columns, and indexes.
Let’s break it down step by step with syntax + real examples
All RENAME Commands in MySQL
1. Rename a Table (Main Use)
Syntax 1 (simple):
RENAME TABLE old_table TO new_table;
Syntax 2 (using ALTER):
ALTER TABLE old_table RENAME TO new_table;
Example:
RENAME TABLE students TO pupils;
-- or
ALTER TABLE students RENAME TO pupils;
The whole register students is now called pupils.
2. Rename Multiple Tables at Once
Syntax:
RENAME TABLE old_table1 TO new_table1,
old_table2 TO new_table2;
Example:
RENAME TABLE teachers TO staff,
classes TO school_classes;
Renames two tables in one command.
3. Rename a Column (MySQL 8+)
Syntax 1 (new way):
ALTER TABLE table_name
RENAME COLUMN old_column TO new_column;
Syntax 2 (older way, also changes datatype):
ALTER TABLE table_name
CHANGE old_column new_column datatype;
Example:
ALTER TABLE pupils RENAME COLUMN full_name TO student_name;
-- or (older way, need datatype)
ALTER TABLE pupils CHANGE full_name student_name VARCHAR(50);
Renames full_name → student_name.
4. Rename an Index
Syntax:
ALTER TABLE table_name
RENAME INDEX old_index_name TO new_index_name;
Example:
ALTER TABLE pupils RENAME INDEX uq_email TO unique_email;
Changes the index name, but keeps the rule.
5. Rename a Database ( Not directly supported)
MySQL doesn’t have a direct RENAME DATABASE command.
But you can simulate it:
Method 1: Export → Import
-- Step 1: Create new DB
CREATE DATABASE new_db;
-- Step 2: Move tables
RENAME TABLE old_db.table1 TO new_db.table1,
old_db.table2 TO new_db.table2;
Method 2: Dump & Restore (using MySQL shell/command line)
mysqldump -u root -p old_db > old_db.sql
mysql -u root -p -e "CREATE DATABASE new_db"
mysql -u root -p new_db < old_db.sql
Example: Rename schooldb → highschool_db.
Summary
• Rename Table → RENAME TABLE old TO new;
• Rename Multiple Tables → RENAME TABLE old1 TO new1, old2 TO new2;
• Rename Column → ALTER TABLE ... RENAME COLUMN old TO new;
• Rename Index → ALTER TABLE ... RENAME INDEX old TO new;
• Rename Database → No direct command → need RENAME TABLE (table by table) or
dump & restore.
Practice Task
1. Create a table books with columns (id, title).
2. Rename table books → library_books.
3. Rename column title → book_title.
4. Create an index on book_title, then rename it.
5. Finally, move library_books to another database schooldb_archive.
3.6 CREATE DATABASE & USE
Before tables, you also create the database (the school building).
Syntax:
CREATE DATABASE database_name;
USE database_name;
Example:
CREATE DATABASE schooldb;
USE schooldb;
Practice Task:
1. Create a database schooldb.
2. Inside it, create a table teachers with:
o teacher_id (INT, PK, AUTO_INCREMENT)
o full_name (VARCHAR(50), NOT NULL)
o subject (VARCHAR(30))
3. Alter the table to add phone (VARCHAR(15), UNIQUE).
4. Rename teachers → staff.
5. Truncate the staff table.
6. Drop the staff table.
DML – Data Manipulation Language
DML commands let us insert, update, delete, and query data inside tables.
Think of it like writing, editing, or erasing student names in a school register (while DDL was
about creating the register).
1. INSERT (Add new data)
Syntax:
INSERT INTO table_name (col1, col2, col3, ...)
VALUES (val1, val2, val3, ...);
Example (School Database):
INSERT INTO students (full_name, age, class, admission_date)
VALUES ('Ali Khan', 12, '5A', '2023-04-01');
Insert multiple rows:
INSERT INTO students (full_name, age, class, admission_date)
VALUES
('Sara Ahmed', 11, '5B', '2023-04-02'),
('John Smith', 13, '6A', '2023-04-05');
2. UPDATE (Change existing data)
Syntax:
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;
Without WHERE, all rows will be updated.
Example:
-- Change class of student Ali Khan to 6A
UPDATE students
SET class = '6A'
WHERE full_name = 'Ali Khan';
Update multiple fields:
UPDATE students
SET age = 14, class = '7B'
WHERE student_id = 3;
UPDATE = change existing data inside a table.
Always use WHERE unless you want to update every row.
UPDATE Command in MySQL
1. Basic UPDATE (Single Column)
UPDATE table_name
SET column_name = value
WHERE condition;
Example:
UPDATE students
SET age = 13
WHERE student_id = 1;
Change age of student with ID 1.
2. Update Multiple Columns
UPDATE table_name
SET column1 = value1, column2 = value2
WHERE condition;
Example:
UPDATE students
SET age = 14, class = '7A'
WHERE full_name = 'Ali Khan';
Update age and class together.
3. Update All Rows ( Use Carefully!)
UPDATE table_name
SET column_name = value;
Example:
UPDATE students
SET class = 'Unassigned';
Every student will now have class = Unassigned.
4. Update with Expression / Calculation
UPDATE table_name
SET column_name = column_name + number
WHERE condition;
Example:
UPDATE students
SET age = age + 1;
Increase every student’s age by 1 (like new academic year ).
5. Update with Default Value
UPDATE table_name
SET column_name = DEFAULT
WHERE condition;
Example:
UPDATE students
SET admission_date = DEFAULT
WHERE student_id = 3;
Reset admission_date to its default.
6. Update Using Subquery
UPDATE table_name
SET column_name = (SELECT value FROM another_table WHERE condition)
WHERE condition;
Example:
UPDATE students
SET class = (SELECT class_name FROM classes WHERE class_id = 2)
WHERE student_id = 5;
Update student’s class based on another table.
7. Update with JOIN
UPDATE table1
JOIN table2 ON table1.col = table2.col
SET table1.col1 = table2.col2
WHERE condition;
Example:
UPDATE students s
JOIN classes c ON s.class = c.class_name
SET s.class = c.new_class_name
WHERE c.year = 2024;
Update student class from another table using join.
8. Update with ORDER BY + LIMIT
UPDATE table_name
SET column_name = value
ORDER BY column_name
LIMIT n;
Example:
UPDATE students
SET class = 'VIP'
ORDER BY age DESC
LIMIT 3;
Assign top 3 oldest students to “VIP” class.
9. Safe Update Mode (MySQL Workbench feature)
If enabled, UPDATE must use a WHERE with key column or LIMIT.
Otherwise, MySQL will refuse to run the query (to prevent accidents).
Example allowed in Safe Mode:
UPDATE students
SET age = 15
WHERE student_id = 10;
Summary
• Single column: SET col = val
• Multiple columns: SET col1 = val1, col2 = val2
• All rows: SET col = val (no WHERE)
• Expression: SET col = col + 1
• Default value: SET col = DEFAULT
• Subquery: SET col = (SELECT …)
• With JOIN: UPDATE t1 JOIN t2 …
• With LIMIT/ORDER: UPDATE … ORDER BY … LIMIT …
3. DELETE (Remove data)
Syntax:
DELETE FROM table_name WHERE condition;
Without WHERE, it deletes all rows (like TRUNCATE, but slower).
Example:
-- Remove student John Smith
DELETE FROM students
WHERE full_name = 'John Smith';
Delete all rows:
DELETE FROM students;
DELETE is a DML command that removes rows from a table.
Unlike TRUNCATE, it can remove specific rows using WHERE.
Unlike DROP, it keeps the table structure.
DELETE Command in MySQL
1. Delete with Condition (Single Row / Some Rows)
DELETE FROM table_name
WHERE condition;
Example:
DELETE FROM students
WHERE student_id = 5;
Removes only the student with ID = 5.
2. Delete Multiple Rows
DELETE FROM table_name
WHERE condition;
Example:
DELETE FROM students
WHERE class = '5A';
Removes all students from class 5A.
3. Delete All Rows (Without Condition)
DELETE FROM table_name;
Example:
DELETE FROM students;
Removes all students but keeps the table structure.
This is slower than TRUNCATE because it deletes row by row.
4. Delete Using ORDER BY + LIMIT
Useful when you want to remove only some rows.
DELETE FROM table_name
ORDER BY column_name
LIMIT n;
Example:
DELETE FROM students
ORDER BY admission_date ASC
LIMIT 2;
Deletes the 2 earliest admitted students.
5. Delete Using JOIN (Multi-table Delete)
Remove rows from one or more tables based on a relationship.
DELETE t1, t2
FROM table1 t1
JOIN table2 t2 ON t1.col = t2.col
WHERE condition;
Example:
DELETE s, m
FROM students s
JOIN marks m ON s.student_id = m.student_id
WHERE s.class = '6A';
Deletes students in class 6A and their marks together.
6. Delete Using Subquery
DELETE FROM table_name
WHERE column_name IN (SELECT column_name FROM another_table WHERE condition);
Example:
DELETE FROM students
WHERE student_id IN (SELECT student_id FROM marks WHERE score < 40);
Removes all students who failed (score < 40).
7. Safe Delete Mode (Workbench Feature)
When Safe Update/Delete mode is enabled, you must include a WHERE with a primary key or
use LIMIT.
Example (allowed in safe mode):
DELETE FROM students WHERE student_id = 10;
Summary
• Delete specific row(s): DELETE FROM table WHERE condition;
• Delete all rows: DELETE FROM table;
• Delete with ORDER + LIMIT: DELETE … ORDER BY … LIMIT …
• Delete with JOIN: DELETE t1, t2 … JOIN …
• Delete with Subquery: DELETE … WHERE col IN (SELECT …)
Practice Task
1. Delete all students from class 7B.
2. Delete only the 2 youngest students.
3. Delete students who scored less than 50 (using subquery).
4. Delete students + their marks together (using JOIN).
4. SELECT (Retrieve data)
Technically also DML (sometimes classified separately as DQL = Data Query Language).
Syntax:
SELECT column1, column2 FROM table_name WHERE condition;
Example:
-- Get all student names and classes
SELECT full_name, class FROM students;
-- Get only students from class 5A
SELECT full_name, age FROM students WHERE class = '5A';
SELECT is used to query/retrieve data from tables.
It can be as simple as “show all rows” or as complex as multi-joins, subqueries, and
aggregations.
All SELECT Syntax in MySQL
1. Select All Columns
SELECT * FROM table_name;
Example:
SELECT * FROM students;
Returns all columns and rows from students.
2. Select Specific Columns
SELECT column1, column2 FROM table_name;
Example:
SELECT full_name, class FROM students;
Shows only name and class.
3. Select With WHERE (Filter Rows)
SELECT * FROM table_name WHERE condition;
Example:
SELECT * FROM students WHERE class = '5A';
Shows only students from class 5A.
4. Select With Aliases (AS)
SELECT column_name AS alias FROM table_name;
Example:
SELECT full_name AS student_name, class AS grade FROM students;
Renames columns in result set.
5. Select Distinct (Unique Values)
SELECT DISTINCT column_name FROM table_name;
Example:
SELECT DISTINCT class FROM students;
Lists all unique classes without duplicates.
6. Select With ORDER BY
SELECT * FROM table_name ORDER BY column_name [ASC|DESC];
Example:
SELECT * FROM students ORDER BY age DESC;
Shows students from oldest → youngest.
7. Select With LIMIT
SELECT * FROM table_name LIMIT n;
SELECT * FROM table_name LIMIT offset, count;
Example:
SELECT * FROM students LIMIT 5; -- first 5 rows
SELECT * FROM students LIMIT 5, 3; -- skip 5, then show next 3
8. Select With BETWEEN, IN, LIKE
SELECT * FROM table_name WHERE column BETWEEN val1 AND val2;
SELECT * FROM table_name WHERE column IN (val1, val2, ...);
SELECT * FROM table_name WHERE column LIKE pattern;
Example:
SELECT * FROM students WHERE age BETWEEN 10 AND 12;
SELECT * FROM students WHERE class IN ('5A', '6B');
SELECT * FROM students WHERE full_name LIKE 'A%'; -- starts with A
9. Select With Aggregate Functions
SELECT COUNT(*), AVG(column), SUM(column), MIN(column), MAX(column)
FROM table_name;
Example:
SELECT COUNT(*) AS total_students, AVG(age) AS avg_age FROM students;
10. Select With GROUP BY
SELECT column, COUNT(*)
FROM table_name
GROUP BY column;
Example:
SELECT class, COUNT(*) AS total_students
FROM students
GROUP BY class;
11. Select With HAVING (filter groups)
SELECT column, COUNT(*)
FROM table_name
GROUP BY column
HAVING COUNT(*) > 5;
Example:
SELECT class, COUNT(*) AS total_students
FROM students
GROUP BY class
HAVING total_students > 10;
12. Select With JOIN
SELECT t1.col, t2.col
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.fk_id;
Example:
SELECT s.full_name, m.score
FROM students s
JOIN marks m ON s.student_id = m.student_id;
13. Select With Subquery
SELECT column FROM table WHERE column IN (SELECT column FROM other_table);
Example:
SELECT full_name FROM students
WHERE student_id IN (SELECT student_id FROM marks WHERE score < 40);
14. Select With CASE (Conditional)
SELECT column,
CASE
WHEN condition THEN result
ELSE other_result
END AS new_column
FROM table_name;
Example:
SELECT full_name, score,
CASE
WHEN score >= 50 THEN 'Pass'
ELSE 'Fail'
END AS result
FROM marks;
Summary of SELECT
• Basic: SELECT * FROM table;
• Specific columns: SELECT col1, col2 …
• Filter: WHERE
• Unique values: DISTINCT
• Sort: ORDER BY
• Limit: LIMIT
• Filter by range/set/pattern: BETWEEN, IN, LIKE
• Aggregates: COUNT, AVG, SUM, MIN, MAX
• Grouping: GROUP BY … HAVING
• Joins: combine tables
• Subqueries: use query inside query
• CASE: conditional column
Practice Task
1. Select all students older than 12.
2. Select only student names (no other columns).
3. Select unique class names.
4. Count how many students are in each class.
5. Show student names with “Pass/Fail” based on marks.
Real-World Practice Scenario (School Database)
1. Add 3 new teachers into teachers table.
2. Update one teacher’s subject from “Math” to “Science”.
3. Delete one teacher who left school.
4. Select all teachers teaching “Science”.
Common SELECT Uses with Real Examples
1. Select All Columns
SELECT * FROM students;
Show all student details.
2. Select Specific Columns
SELECT full_name, class FROM students;
Show only student name and class.
3. Filtering with WHERE
SELECT * FROM students
WHERE age > 12;
Show only students older than 12.
4. Remove Duplicates (DISTINCT)
SELECT DISTINCT class FROM students;
Show unique classes.
5. Sorting Results (ORDER BY)
SELECT * FROM students
ORDER BY age DESC;
Oldest students first.
6. Limit Number of Results
SELECT * FROM students
LIMIT 5;
Show only 5 students.
LIMIT in MySQL
LIMIT is used with SELECT to restrict the number of rows returned.
Often combined with ORDER BY to get Top N or Bottom N results.
Syntax
1. Limit First N Rows
SELECT column1, column2, ...
FROM table_name
LIMIT n;
2. Limit with Offset
SELECT column1, column2, ...
FROM table_name
LIMIT offset, count;
• offset → how many rows to skip
• count → how many rows to fetch after skipping
Real-World Examples (School Database)
Suppose we have a table:
CREATE TABLE students (
student_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(50),
class VARCHAR(10),
age INT,
score INT
);
INSERT INTO students (full_name, class, age, score) VALUES
('Amit Sharma', '5A', 11, 85),
('Anita Singh', '6B', 12, 92),
('Arun Mehta', '5A', 13, 76),
('Bhavna Patel', '7A', 12, 88),
('Chetan Das', '6B', 14, 67),
('David Roy', '5A', 11, 95),
('Aryan Gupta', '7B', 12, 72);
1. Show First 3 Students
SELECT * FROM students
LIMIT 3;
Returns only the first 3 rows.
2. Skip First 2, Show Next 3
SELECT * FROM students
LIMIT 2, 3;
Skips 2 rows, then shows 3 rows.
3. Get Top 3 Scorers
SELECT full_name, score
FROM students
ORDER BY score DESC
LIMIT 3;
Shows David, Anita, Bhavna (highest scores).
4. Get Youngest 2 Students
SELECT full_name, age
FROM students
ORDER BY age ASC
LIMIT 2;
Shows Amit, David (youngest students).
5. Paginate Results (For Web Apps)
Used when showing data in pages (like Google search results).
• Page 1 (rows 1–3):
SELECT * FROM students
LIMIT 0, 3;
• Page 2 (rows 4–6):
SELECT * FROM students
LIMIT 3, 3;
Summary
• LIMIT n → get first n rows
• LIMIT offset, count → skip offset rows, then fetch count rows
• Combine with ORDER BY → useful for Top N / Bottom N queries
• Used for pagination in real-world apps
Practice Task
1. Show only first 5 students.
2. Show the 2nd page of results if each page has 2 students.
3. Show the Top 2 scorers.
4. Show the 3 youngest students.
7. Pattern Matching (LIKE, %, _)
SELECT * FROM students
WHERE full_name LIKE 'A%';
Names starting with "A".
(% = any length, _ = one character)
Pattern Matching in SQL (MySQL)
We use the LIKE operator with wildcards (% and _) to search for text patterns inside string
columns.
Syntax
SELECT column1, column2, ...
FROM table_name
WHERE column_name LIKE pattern;
• % → matches 0 or more characters
• _ → matches exactly 1 character
Real-World Examples (School Database)
Suppose we have a table:
CREATE TABLE students (
student_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(50),
class VARCHAR(10),
age INT
);
INSERT INTO students (full_name, class, age) VALUES
('Amit Sharma', '5A', 11),
('Anita Singh', '6B', 12),
('Arun Mehta', '5A', 13),
('Bhavna Patel', '7A', 12),
('Chetan Das', '6B', 14),
('David Roy', '5A', 11),
('Aryan Gupta', '7B', 12);
1. Names Starting with "A"
SELECT full_name FROM students
WHERE full_name LIKE 'A%';
Finds Amit, Anita, Arun, Aryan.
2. Names Ending with "a"
SELECT full_name FROM students
WHERE full_name LIKE '%a';
Finds Anita, Bhavna.
3. Names Containing "an"
SELECT full_name FROM students
WHERE full_name LIKE '%an%';
Finds Anita, Bhavna, Chetan, Aryan.
4. Second Letter is "r"
SELECT full_name FROM students
WHERE full_name LIKE '_r%';
Finds names like Arun, Aryan.
5. Names With Exactly 5 Characters
SELECT full_name FROM students
WHERE full_name LIKE '_____';
Finds names like David (exactly 5 letters).
6. Names Starting with A and Ending with n
SELECT full_name FROM students
WHERE full_name LIKE 'A%n';
Finds Arun, Aryan.
7. Class Pattern Matching
SELECT * FROM students
WHERE class LIKE '5_';
Matches classes like 5A, 5B, 5C (any single character after 5).
8. Escape Character (When Searching Special Symbols)
SELECT * FROM students
WHERE full_name LIKE '%\%%';
Finds names containing the % symbol (rare, but possible in real data).
Summary
• LIKE 'A%' → starts with A
• LIKE '%a' → ends with a
• LIKE '%an%' → contains "an"
• LIKE '_r%' → second letter is r
• LIKE '_____' → exactly 5 characters
• LIKE '5_' → matches 5A, 5B, etc.
Practice Task for You
1. Find all students whose name starts with "D".
2. Find students whose name ends with "n".
3. Find all students with 3-letter names.
4. Find all students in classes that begin with "6".
5. Find names that contain "it" anywhere.
8. Range & Set Filters (BETWEEN, IN)
SELECT * FROM students
WHERE age BETWEEN 10 AND 12;
SELECT * FROM students
WHERE class IN ('5A', '6B');
Students aged 10–12 OR in 5A/6B.
Range & Set Filters in MySQL
BETWEEN (Range Filter)
Used to filter values within a range (numbers, dates, or text).
Syntax
SELECT column1, column2
FROM table_name
WHERE column_name BETWEEN value1 AND value2;
• Includes both boundary values (value1 & value2).
Real-World Example (School Database)
Suppose our students table:
CREATE TABLE students (
student_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(50),
class VARCHAR(10),
age INT,
score INT
);
INSERT INTO students (full_name, class, age, score) VALUES
('Amit Sharma', '5A', 11, 85),
('Anita Singh', '6B', 12, 92),
('Arun Mehta', '5A', 13, 76),
('Bhavna Patel', '7A', 12, 88),
('Chetan Das', '6B', 14, 67),
('David Roy', '5A', 11, 95),
('Aryan Gupta', '7B', 12, 72);
a) Students with Age Between 12 and 14
SELECT full_name, age
FROM students
WHERE age BETWEEN 12 AND 14;
Shows Anita, Arun, Bhavna, Chetan, Aryan.
b) Students Who Scored Between 70 and 90
SELECT full_name, score
FROM students
WHERE score BETWEEN 70 AND 90;
Shows Arun, Bhavna, Aryan, Amit.
c) Students Admitted Between 2022-01-01 and 2022-12-31 (Date Example)
SELECT full_name, admission_date
FROM students
WHERE admission_date BETWEEN '2022-01-01' AND '2022-12-31';
IN (Set Filter)
Used to filter values from a list of possible values.
Syntax
SELECT column1, column2
FROM table_name
WHERE column_name IN (value1, value2, value3, ...);
Real-World Examples
a) Students in Specific Classes
SELECT full_name, class
FROM students
WHERE class IN ('5A', '7B');
Shows students only from 5A and 7B.
b) Students with Specific IDs
SELECT full_name
FROM students
WHERE student_id IN (1, 3, 5);
Shows students with IDs 1, 3, 5.
c) Students Whose Score Is in a Given Set
SELECT full_name, score
FROM students
WHERE score IN (67, 85, 95);
Shows students who scored exactly 67, 85, or 95.
NOT BETWEEN & NOT IN
Exclude ranges or sets.
a) Students Not Between Age 12 and 14
SELECT full_name, age
FROM students
WHERE age NOT BETWEEN 12 AND 14;
Shows only ages outside 12–14.
b) Students Not in Classes 5A or 6B
SELECT full_name, class
FROM students
WHERE class NOT IN ('5A', '6B');
Summary
• BETWEEN val1 AND val2 → within range (inclusive).
• IN (val1, val2, val3) → matches any of the listed values.
• NOT BETWEEN / NOT IN → excludes ranges or sets.
Practice Tasks
1. Find students with scores between 70 and 90.
2. Find students whose class is either 5A or 7A.
3. Find students with age NOT between 11 and 12.
4. Find students whose score is in (76, 88, 95).
BETWEEN vs >= AND <=
Both are used for filtering ranges of values.
BETWEEN
Shortcut for writing greater than or equal to (>=) AND less than or equal to (<=).
Inclusive → includes both boundary values.
Syntax
SELECT * FROM table_name
WHERE column_name BETWEEN value1 AND value2;
This is exactly the same as:
SELECT * FROM table_name
WHERE column_name >= value1 AND column_name <= value2;
Real-World Examples (Students Table)
student_id | full_name | age | score
-----------+---------------+-----+------
1 | Amit Sharma | 11 | 85
2 | Anita Singh | 12 | 92
3 | Arun Mehta | 13 | 76
4 | Bhavna Patel | 12 | 88
5 | Chetan Das | 14 | 67
6 | David Roy | 11 | 95
7 | Aryan Gupta | 12 | 72
Using BETWEEN
SELECT full_name, age
FROM students
WHERE age BETWEEN 12 AND 14;
Returns ages 12, 13, 14 → Anita, Arun, Bhavna, Chetan, Aryan.
Using >= AND <= (Same Result)
SELECT full_name, age
FROM students
WHERE age >= 12 AND age <= 14;
Same result as above.
Key Differences in Practice
(a) Readability
• BETWEEN 12 AND 14 → shorter, cleaner.
• >= 12 AND <= 14 → longer, but more flexible.
(b) When Boundaries Aren’t Fixed
Example: exclude the boundary value 12.
-- Using BETWEEN (not possible without NOT)
WHERE age BETWEEN 12 AND 14; -- includes 12 & 14
-- Using >= AND <= (flexible)
WHERE age > 12 AND age <= 14; -- excludes 12
So >= AND <= is more flexible if you need exclusive ranges.
(c) Dates Example
-- BETWEEN (includes start and end date)
WHERE admission_date BETWEEN '2022-01-01' AND '2022-12-31';
-- Equivalent with >= AND <=
WHERE admission_date >= '2022-01-01' AND admission_date <= '2022-12-31';
Both give the same results, but again >= AND <= is better if you want to exclude end date.
Summary
• BETWEEN val1 AND val2
Short, easy to read
Always inclusive of boundaries
• >= val1 AND <= val2
Same as BETWEEN
More flexible → allows exclusive ranges (>, <)
Practice Task for You
1. Write a query to get students with age between 11 and 13 (inclusive) using both
methods.
2. Get students with score greater than 70 but less than 90 (exclusive).
3. Get students admitted between Jan 1 and Jun 30, 2023.
9. Aggregate Functions
SELECT COUNT(*) AS total_students,
AVG(age) AS avg_age,
MAX(age) AS oldest
FROM students;
Total students, average age, oldest student.
Aggregate Functions in SQL (MySQL)
Aggregate functions work on a set of rows and return a single value.
Usually used with SELECT and often combined with GROUP BY.
COUNT()
Counts the number of rows.
Syntax:
SELECT COUNT(column_name) FROM table_name;
SELECT COUNT(*) FROM table_name; -- counts all rows
Example:
SELECT COUNT(*) AS total_students
FROM students;
Gives total number of students.
SUM()
Adds up numeric values.
Syntax:
SELECT SUM(column_name) FROM table_name;
Example:
SELECT SUM(score) AS total_score
FROM students;
Gives sum of all students’ scores.
AVG()
Finds the average of values.
Syntax:
SELECT AVG(column_name) FROM table_name;
Example:
SELECT AVG(score) AS avg_score
FROM students;
Finds average score of all students.
MIN()
Finds the smallest value.
Syntax:
SELECT MIN(column_name) FROM table_name;
Example:
SELECT MIN(score) AS lowest_score
FROM students;
Returns the lowest score.
MAX()
Finds the largest value.
Syntax:
SELECT MAX(column_name) FROM table_name;
Example:
SELECT MAX(score) AS highest_score
FROM students;
Returns the highest score.
GROUP_CONCAT() (MySQL Specific)
Combines values from a group into a single string.
Syntax:
SELECT GROUP_CONCAT(column_name) FROM table_name;
Example:
SELECT class, GROUP_CONCAT(full_name) AS students_in_class
FROM students
GROUP BY class;
Lists students of each class in a single row.
Combining Aggregate Functions with GROUP BY
Example: Count students per class
SELECT class, COUNT(*) AS total_students
FROM students
GROUP BY class;
Summary of Aggregate Functions
Function Purpose Example
COUNT() Counts rows COUNT(*) = total students
SUM() Adds values SUM(score) = total marks
AVG() Average value AVG(age) = average student age
MIN() Smallest value MIN(score) = lowest marks
MAX() Largest value MAX(score) = highest marks
GROUP_CONCAT() Join values into string (MySQL only) Students per class
Practice Tasks
1. Find the total number of students.
2. Find the average age of students.
3. Find the highest and lowest score.
4. Find the total score of students in class 5A.
5. List all students per class using GROUP_CONCAT.
Aggregate Functions + DISTINCT
Normally, aggregate functions work on all values (including duplicates).
Adding DISTINCT makes them work only on unique values.
COUNT(DISTINCT …)
Counts only unique values.
Example:
SELECT COUNT(class) AS total_classes
FROM students;
Counts rows, including duplicates → if 10 students in class 5A, it counts all 10.
SELECT COUNT(DISTINCT class) AS unique_classes
FROM students;
Counts only unique classes (5A, 6B, 7A …).
SUM(DISTINCT …)
Adds only unique values (ignores duplicates).
Example:
SELECT SUM(score) AS total_score FROM students;
SELECT SUM(DISTINCT score) AS total_unique_score FROM students;
If scores = [85, 92, 76, 88, 67, 95, 72, 85],
• SUM(score) = adds all (including both 85s).
• SUM(DISTINCT score) = adds each unique score once.
AVG(DISTINCT …)
Averages only unique values.
Example:
SELECT AVG(score) AS avg_score FROM students;
SELECT AVG(DISTINCT score) AS avg_unique_score FROM students;
If a score is repeated (like 85 twice), the second query ignores duplicates.
MIN() and MAX()
DISTINCT has no effect because the smallest/largest is the same whether duplicates exist
or not.
Example:
SELECT MIN(DISTINCT score) FROM students; -- same as MIN(score)
SELECT MAX(DISTINCT score) FROM students; -- same as MAX(score)
GROUP_CONCAT(DISTINCT …)
Returns only unique values in the list.
Example:
SELECT GROUP_CONCAT(DISTINCT class) AS classes
FROM students;
If students = [5A, 5A, 6B, 7A], result = 5A,6B,7A (not repeating 5A).
Quick Summary
Function Without DISTINCT With DISTINCT
COUNT(col) Counts all rows (including dups) Counts only unique values
SUM(col) Adds all values Adds only unique values
AVG(col) Average of all values Average of unique values
MIN/MAX(col) Same with or without DISTINCT No difference
GROUP_CONCAT(col) Lists all values (duplicates too) Lists only unique values
Practice Tasks
1. Count how many unique classes exist.
2. Find the total of unique scores.
3. Find the average of unique scores.
4. Show unique class names using GROUP_CONCAT.
SQL Functions (String + Date)
Functions are always used inside SELECT, WHERE, ORDER BY, etc.
They take input (argument) and return a result.
String Functions
Work with text columns (like names, addresses).
Common String Functions (MySQL)
Function Purpose Example
Function Purpose Example
UPPER() Convert text to uppercase UPPER('hello') → HELLO
LOWER() Convert text to lowercase LOWER('HELLO') → hello
LENGTH() Returns length of string LENGTH('Amit') → 4
CONCAT() Join strings together CONCAT('Amit',' Sharma') → Amit Sharma
CONCAT_WS() Join with separator CONCAT_WS('-', '2023','09','04') → 2023-09-04
SUBSTRING() Extract part of string SUBSTRING('Database', 1, 4) → Data
LEFT() Extract left part LEFT('Database', 4) → Data
RIGHT() Extract right part RIGHT('Database', 4) → base
TRIM() Remove spaces TRIM(' hello ') → hello
REPLACE() Replace text REPLACE('I like Java', 'Java', 'SQL') → I like SQL
REVERSE() Reverse text REVERSE('SQL') → LQS
Example with students table:
SELECT
full_name,
UPPER(full_name) AS upper_name,
LENGTH(full_name) AS name_length,
LEFT(full_name, 5) AS first_5_chars
FROM students;
Returns each student’s name in uppercase, length, and first 5 characters.
Date & Time Functions
Work with date and time values.
Common Date Functions (MySQL)
Function Purpose Example
Current date &
NOW() 2025-09-04 12:30:00
time
CURDATE() Current date only 2025-09-04
Function Purpose Example
CURTIME() Current time only 12:30:00
YEAR(date) Extract year YEAR('2023-09-04') → 2023
Extract month
MONTH(date) MONTH('2023-09-04') → 9
number
Extract month
MONTHNAME(date) MONTHNAME('2023-09-04') → September
name
DAY(date) Extract day DAY('2023-09-04') → 4
DAYNAME(date) Extract weekday DAYNAME('2023-09-04') → Monday
DATEDIFF(d1,d2) Difference in days DATEDIFF('2023-09-10','2023-09-04') → 6
DATE_ADD(date, INTERVAL x DATE_ADD('2023-09-04', INTERVAL 10 DAY) →
Add interval
unit) 2023-09-14
DATE_SUB(date, INTERVAL x DATE_SUB('2023-09-04', INTERVAL 1 MONTH)
Subtract interval
unit) → 2023-08-04
LAST_DAY(date) Last day of month LAST_DAY('2023-09-04') → 2023-09-30
Example with students (assume column admission_date):
SELECT
full_name,
admission_date,
YEAR(admission_date) AS admission_year,
MONTHNAME(admission_date) AS admission_month,
DATEDIFF(NOW(), admission_date) AS days_since_admission
FROM students;
Returns each student’s admission year, month, and how many days since they joined.
Real-World Example (School)
Suppose we have students table:
student_id | full_name | admission_date | score
-----------+-------------+----------------+------
1 | Amit Sharma | 2022-04-15 | 85
2 | Anita Singh | 2023-01-20 | 92
3 | Arun Mehta | 2023-05-10 | 76
Example Queries:
-- Convert names to uppercase
SELECT UPPER(full_name) FROM students;
-- Find year of admission
SELECT full_name, YEAR(admission_date) AS year FROM students;
-- Count days since admission
SELECT full_name, DATEDIFF(NOW(), admission_date) AS days_in_school FROM students;
Summary
• String Functions → Work with text (UPPER, LOWER, CONCAT, SUBSTRING, LENGTH,
etc.)
• Date Functions → Work with dates (NOW, CURDATE, YEAR, MONTHNAME, DATEDIFF,
DATE_ADD, etc.)
Practice for You
1. Show student names in uppercase and their name length.
2. Show admission year and month name of each student.
3. Find how many days each student has been in school.
4. Concatenate student name with admission year like → Amit Sharma (2022).
Numeric Functions in SQL
ROUND()
Rounds a number to given decimal places.
Syntax:
ROUND(number, decimals)
Example:
SELECT ROUND(85.567, 2) AS rounded; -- 85.57
SELECT ROUND(85.567, 0) AS rounded; -- 86
CEIL() / CEILING()
Rounds number upward to nearest integer.
Example:
SELECT CEIL(85.1) AS ceil; -- 86
SELECT CEIL(85.9) AS ceil; -- 86
FLOOR()
Rounds number downward to nearest integer.
Example:
SELECT FLOOR(85.9) AS floor; -- 85
SELECT FLOOR(85.1) AS floor; -- 85
TRUNCATE()
Cuts off decimal digits without rounding.
Syntax:
TRUNCATE(number, decimals)
Example:
SELECT TRUNCATE(85.678, 2); -- 85.67
SELECT TRUNCATE(85.678, 0); -- 85
MOD()
Returns remainder after division.
Syntax:
MOD(number, divisor)
Example:
SELECT MOD(10, 3); -- 1
SELECT MOD(25, 7); -- 4
POWER() / POW()
Raises a number to the power of another.
Example:
SELECT POWER(2, 3); -- 8 (2³)
SELECT POW(5, 2); -- 25
SQRT()
Square root of a number.
Example:
SELECT SQRT(81); -- 9
ABS()
Returns absolute (positive) value.
Example:
SELECT ABS(-25); -- 25
SELECT ABS(25); -- 25
SIGN()
Returns sign of number.
• 1 if positive
• 0 if zero
• -1 if negative
Example:
SELECT SIGN(50); -- 1
SELECT SIGN(0); -- 0
SELECT SIGN(-20); -- -1
RAND()
Returns random number between 0 and 1.
Example:
SELECT RAND();
SELECT FLOOR(RAND() * 100) + 1 AS random_number; -- random 1 to 100
Real World Examples (School Database)
Assume students table:
student_id | full_name | score
-----------+--------------+------
1 | Amit Sharma | 85.67
2 | Anita Singh | 92.33
3 | Arun Mehta | 76.55
Examples:
-- Round student scores to nearest integer
SELECT full_name, ROUND(score, 0) AS rounded_score
FROM students;
-- Get highest integer score (ceil)
SELECT full_name, CEIL(score) AS ceil_score
FROM students;
-- Get lowest integer score (floor)
SELECT full_name, FLOOR(score) AS floor_score
FROM students;
-- Show score squared
SELECT full_name, POWER(score, 2) AS score_squared
FROM students;
-- Show random roll number between 1000 and 2000
SELECT full_name, FLOOR(RAND() * 1000) + 1000 AS roll_no
FROM students;
Summary
Function Purpose Example
ROUND(x,d) Round to d decimals ROUND(85.567,2)=85.57
CEIL(x) Round up CEIL(85.1)=86
FLOOR(x) Round down FLOOR(85.9)=85
TRUNCATE(x,d) Cut decimals TRUNCATE(85.678,2)=85.67
MOD(a,b) Remainder MOD(10,3)=1
POWER(a,b) Exponent POWER(2,3)=8
SQRT(x) Square root SQRT(81)=9
ABS(x) Absolute value ABS(-25)=25
SIGN(x) Sign of number SIGN(-5)=-1
RAND() Random 0–1 RAND()*100 = random 0–100
Practice for You
1. Show student scores rounded to 1 decimal place.
2. Show each score’s absolute difference from 80.
3. Generate a random roll number for each student (1000–9999).
4. Show square root of each score.
Conditional Functions in SQL (MySQL)
IF(condition, true_value, false_value)
Works like an if-else statement.
Syntax:
IF(condition, value_if_true, value_if_false)
Example:
SELECT full_name,
score,
IF(score >= 80, 'Pass', 'Fail') AS result
FROM students;
If score >= 80, returns "Pass", otherwise "Fail".
IFNULL(expression, value_if_null)
Replaces NULL with another value.
Syntax:
IFNULL(expression, value_if_null)
Example:
SELECT full_name,
IFNULL(score, 0) AS safe_score
FROM students;
If score is NULL, it will show 0.
NULLIF(expr1, expr2)
Returns NULL if both expressions are equal, otherwise returns expr1.
Syntax:
NULLIF(expr1, expr2)
Example:
SELECT NULLIF(100, 100); -- NULL
SELECT NULLIF(90, 100); -- 90
Useful to avoid division by zero:
SELECT score / NULLIF(total_classes, 0) AS avg_per_class
FROM students;
COALESCE(expr1, expr2, expr3, …)
Returns the first non-NULL value in the list.
Syntax:
COALESCE(expr1, expr2, expr3, ...)
Example:
SELECT full_name,
COALESCE(phone, email, 'Not Available') AS contact
FROM students;
If phone is NULL → take email, if both are NULL → "Not Available".
CASE WHEN … THEN … ELSE … END
The most powerful conditional function → works like switch-case.
Syntax:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE default_result
END
Example 1: Grade Students
SELECT full_name, score,
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 75 THEN 'B'
WHEN score >= 50 THEN 'C'
ELSE 'Fail'
END AS grade
FROM students;
Example 2: Category by Admission Year
SELECT full_name, admission_date,
CASE YEAR(admission_date)
WHEN 2023 THEN 'New Student'
WHEN 2022 THEN 'Old Student'
ELSE 'Senior'
END AS category
FROM students;
Real-World Example (students table)
student_id | full_name | score | phone | admission_date
-----------+--------------+-------+-------------+----------------
1 | Amit Sharma | 85 | NULL | 2022-04-15
2 | Anita Singh | 92 | 9876543210 | 2023-01-20
3 | Arun Mehta | 45 | NULL | 2023-05-10
Queries:
-- Pass/Fail with IF()
SELECT full_name, IF(score >= 50, 'Pass', 'Fail') AS result FROM students;
-- Handle missing phone numbers with COALESCE
SELECT full_name, COALESCE(phone, 'No Phone') AS contact FROM students;
-- Assign grade using CASE
SELECT full_name, score,
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 75 THEN 'B'
WHEN score >= 50 THEN 'C'
ELSE 'Fail'
END AS grade
FROM students;
Summary
Function Purpose Example
IF() Simple if-else IF(score>=50,'Pass','Fail')
IFNULL() Replace NULL with value IFNULL(score,0)
NULLIF() NULL if equal NULLIF(100,100)=NULL
COALESCE() First non-NULL value COALESCE(phone,email,'N/A')
CASE WHEN Complex conditions Grading, categorization
Practice for You
1. Show all students with "Pass" or "Fail" using IF().
2. Replace NULL phone numbers with "Not Available" using IFNULL.
3. Use CASE to give grades A/B/C/Fail based on score.
4. Show contact info using COALESCE(phone, email, 'No Contact').
10. Grouping (GROUP BY + HAVING)
SELECT class, COUNT(*) AS total_students
FROM students
GROUP BY class
HAVING COUNT(*) > 10;
Classes with more than 10 students.
11. Joining Another Table
SELECT s.full_name, m.score
FROM students s
JOIN marks m ON s.student_id = m.student_id;
Show each student with their score.
SQL JOINS (MySQL)
When data is split across tables (to avoid duplication), we use joins to bring them together.
Joins work based on a common column (usually a Primary Key / Foreign Key).
INNER JOIN
Returns only matching rows from both tables.
Syntax:
SELECT columns
FROM table1
INNER JOIN table2
ON table1.common_column = table2.common_column;
Example: Students & Classes
SELECT s.full_name, c.class_name
FROM students s
INNER JOIN classes c
ON s.class_id = c.class_id;
Shows only students who are assigned to a class.
LEFT JOIN (or LEFT OUTER JOIN)
Returns all rows from left table, and matching rows from right table.
If no match → NULL.
Syntax:
SELECT columns
FROM table1
LEFT JOIN table2
ON table1.common_column = table2.common_column;
Example:
SELECT s.full_name, c.class_name
FROM students s
LEFT JOIN classes c
ON s.class_id = c.class_id;
Shows all students, even if they don’t belong to any class (class_name = NULL).
RIGHT JOIN (or RIGHT OUTER JOIN)
Returns all rows from right table, and matching rows from left table.
If no match → NULL.
Syntax:
SELECT columns
FROM table1
RIGHT JOIN table2
ON table1.common_column = table2.common_column;
Example:
SELECT s.full_name, c.class_name
FROM students s
RIGHT JOIN classes c
ON s.class_id = c.class_id;
Shows all classes, even if no student is assigned (student = NULL).
FULL JOIN (or FULL OUTER JOIN)
Returns all rows when there is a match in either table.
MySQL doesn’t support FULL JOIN directly → we simulate using UNION.
Syntax:
SELECT columns
FROM table1
LEFT JOIN table2 ON ...
UNION
SELECT columns
FROM table1
RIGHT JOIN table2 ON ...;
Example:
SELECT s.full_name, c.class_name
FROM students s
LEFT JOIN classes c ON s.class_id = c.class_id
UNION
SELECT s.full_name, c.class_name
FROM students s
RIGHT JOIN classes c ON s.class_id = c.class_id;
CROSS JOIN
Returns all possible combinations (Cartesian product).
No condition required.
Syntax:
SELECT *
FROM table1
CROSS JOIN table2;
Example:
SELECT s.full_name, c.class_name
FROM students s
CROSS JOIN classes c;
If 3 students × 2 classes → 6 rows.
Example Tables
students
student_id | full_name | class_id
-----------+--------------+---------
1 | Amit Sharma | 101
2 | Anita Singh | 102
3 | Arun Mehta | NULL
classes
class_id | class_name
---------+------------
101 | Class 5A
102 | Class 6B
103 | Class 7A
Results:
• INNER JOIN → Amit (5A), Anita (6B)
• LEFT JOIN → Amit (5A), Anita (6B), Arun (NULL)
• RIGHT JOIN → Amit (5A), Anita (6B), (NULL, 7A)
• FULL JOIN → All of the above
• CROSS JOIN → every student with every class
Summary
Join Type Returns
INNER JOIN Matching rows only
LEFT JOIN All left rows + matching right
RIGHT JOIN All right rows + matching left
FULL JOIN All rows from both (MySQL uses UNION)
CROSS JOIN All possible combinations
Practice for You
1. Show all students with their class names (use LEFT JOIN).
2. Show all classes with their students (use RIGHT JOIN).
3. List only students who are assigned to a class (INNER JOIN).
4. Combine all students and all classes (FULL JOIN using UNION).
5. Generate all possible student-class combinations (CROSS JOIN).
Self Join
A self join means joining a table with itself.
Useful when data in a table is related to other rows in the same table (hierarchy,
relationships).
You must use aliases (a, b) to differentiate the two “copies” of the same table.
Syntax
SELECT a.column, b.column
FROM table_name a
JOIN table_name b
ON a.common_column = b.related_column;
Real World Example
Suppose we have a students table where each student may have a mentor (who is also a
student).
students table:
student_id | full_name | mentor_id
-----------+--------------+-----------
1 | Amit Sharma | NULL
2 | Anita Singh | 1
3 | Arun Mehta | 2
4 | Bhavna Patel | 1
• mentor_id points to another student_id in the same table.
Query: Student with Mentor Name
SELECT s.full_name AS student,
m.full_name AS mentor
FROM students s
LEFT JOIN students m
ON s.mentor_id = m.student_id;
Result:
student mentor
Amit Sharma NULL
Anita Singh Amit Sharma
Arun Mehta Anita Singh
Bhavna Patel Amit Sharma
Another Example: Employees
Imagine an employees table:
emp_id | emp_name | manager_id
-------+------------+-----------
1 | Rahul | NULL
2 | Sita |1
3 | Mohan |1
4 | Geeta |2
Query: Employee with Manager Name
SELECT e.emp_name AS employee,
m.emp_name AS manager
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.emp_id;
Output:
employee manager
Rahul NULL
Sita Rahul
Mohan Rahul
Geeta Sita
When to Use Self Join?
• Students & mentors (both in same table)
• Employees & managers
• Categories & subcategories
• Family tree relationships (parent-child)
Practice for You
1. Write a query to show student and their mentor.
2. Modify query to show only students who have mentors (use INNER JOIN).
3. In an employee table, show each employee along with their manager’s name.
4. Create a query that shows category → subcategory from a category table with
parent_id.
12. Subquery Inside SELECT
SELECT full_name,
(SELECT AVG(score) FROM marks WHERE marks.student_id = students.student_id) AS
avg_score
FROM students;
Show each student with their average score.
13. Conditional Output (CASE)
SELECT full_name, score,
CASE
WHEN score >= 50 THEN 'Pass'
ELSE 'Fail'
END AS result
FROM marks;
Show Pass/Fail instead of just marks.
Real-World Practice Task (School Database)
1. Show all students in class = '5A'.
2. Show only names of students older than 12.
3. Count how many students are in each class.
4. List all unique classes.
5. Show top 3 scorers from marks table.
6. Show student names with “Pass/Fail” labels.
Summary of DML Commands
• INSERT → Add new data (new student record).
• UPDATE → Modify existing data (update student age/class).
• DELETE → Remove specific data (remove a student).
• SELECT → Retrieve data (get list of students).
GROUP BY & HAVING in SQL
GROUP BY
Groups rows that have the same values into summary rows.
Often used with aggregate functions (COUNT, SUM, AVG, MAX, MIN).
Syntax:
SELECT column, AGG_FUNC(column)
FROM table_name
GROUP BY column;
Example (Students Table)
student_id | full_name | class_id | score
-----------+--------------+----------+------
1 | Amit Sharma | 101 | 85
2 | Anita Singh | 102 | 92
3 | Arun Mehta | 101 | 76
4 | Bhavna Patel | 103 | 88
5 | Chetan Das | 102 | 67
Query: Count students in each class
SELECT class_id, COUNT(*) AS total_students
FROM students
GROUP BY class_id;
Result:
class_id total_students
101 2
102 2
103 1
Query: Average score per class
SELECT class_id, AVG(score) AS avg_score
FROM students
GROUP BY class_id;
Result:
class_id avg_score
101 80.5
102 79.5
103 88
HAVING
Works like WHERE, but is used to filter groups (after GROUP BY).
WHERE → filters rows before grouping
HAVING → filters groups after grouping
Syntax:
SELECT column, AGG_FUNC(column)
FROM table_name
GROUP BY column
HAVING condition;
Example: Show only classes with more than 1 student
SELECT class_id, COUNT(*) AS total_students
FROM students
GROUP BY class_id
HAVING COUNT(*) > 1;
Result:
class_id total_students
101 2
102 2
Example: Show classes where average score > 80
SELECT class_id, AVG(score) AS avg_score
FROM students
GROUP BY class_id
HAVING AVG(score) > 80;
Result:
class_id avg_score
103 88
Difference Between WHERE and HAVING
Feature WHERE HAVING
Filters rows Yes No
Filters groups No Yes
Used with aggregates No Yes
Execution Before grouping After grouping
Example:
-- WHERE filters rows first
SELECT class_id, AVG(score)
FROM students
WHERE score > 70
GROUP BY class_id;
-- HAVING filters grouped results
SELECT class_id, AVG(score)
FROM students
GROUP BY class_id
HAVING AVG(score) > 70;
Summary
• GROUP BY → Groups rows by column(s).
• HAVING → Filters grouped results (like WHERE but for groups).
• Often used with COUNT, SUM, AVG, MIN, MAX.
Practice for You
1. Find total number of students per class.
2. Show average score per class.
3. Show only classes with more than 2 students.
4. Show classes where the highest score > 90.
5. Show classes where average score is between 70 and 85.
12) Subqueries & CTE
Subqueries (Nested Queries)
A subquery is a query inside another query.
Can be used in:
• WHERE (to filter)
• FROM (as a derived table)
• SELECT (to return a single value)
Syntax
SELECT column
FROM table
WHERE column OPERATOR (SELECT column FROM table WHERE condition);
Example 1: Subquery in WHERE
Find students who scored above the average score.
SELECT full_name, score
FROM students
WHERE score > (SELECT AVG(score) FROM students);
Subquery (SELECT AVG(score) …) finds the average.
Outer query selects students scoring higher.
Example 2: Subquery in SELECT
Find each student’s score and compare with class average.
SELECT full_name,
score,
(SELECT AVG(score) FROM students) AS class_avg
FROM students;
Example 3: Subquery in FROM (Derived Table)
Find top scorers by class.
SELECT class_id, MAX(score) AS top_score
FROM students
GROUP BY class_id;
Now use this as a subquery in FROM:
SELECT s.class_id, s.full_name, s.score
FROM students s
JOIN (
SELECT class_id, MAX(score) AS top_score
FROM students
GROUP BY class_id
)t
ON s.class_id = t.class_id AND s.score = t.top_score;
Shows student(s) with top score per class.
CTE (Common Table Expression)
CTE is like a temporary result set you can reference multiple times in the query.
Improves readability over nested subqueries.
Uses WITH keyword.
Syntax
WITH cte_name AS (
SELECT ...
SELECT ...
FROM cte_name;
Example 1: Simple CTE
Find average score first, then use it.
WITH avg_cte AS (
SELECT AVG(score) AS avg_score FROM students
SELECT full_name, score
FROM students, avg_cte
WHERE students.score > avg_cte.avg_score;
Same as subquery, but clearer.
Example 2: Multiple CTEs
Find total students per class and then filter classes with >2 students.
WITH class_count AS (
SELECT class_id, COUNT(*) AS total_students
FROM students
GROUP BY class_id
SELECT *
FROM class_count
WHERE total_students > 2;
Example 3: Recursive CTE (Advanced)
Useful for hierarchical data (e.g., employees → managers).
WITH RECURSIVE emp_hierarchy AS (
SELECT emp_id, emp_name, manager_id
FROM employees
WHERE manager_id IS NULL -- Start with top manager
UNION ALL
SELECT e.emp_id, e.emp_name, e.manager_id
FROM employees e
INNER JOIN emp_hierarchy h ON e.manager_id = h.emp_id
SELECT * FROM emp_hierarchy;
This prints the entire org chart.
Difference: Subquery vs CTE
Feature Subquery (Nested) CTE (WITH)
Readability Harder if nested deeply Easier & cleaner
Reuse Cannot reuse results Can reuse multiple times
Recursive No Yes (WITH RECURSIVE)
Practice for You
1. Write a query to find students whose score is above the class average (subquery).
2. Show students with the highest score per class (subquery in FROM).
3. Use a CTE to find classes with more than 2 students.
4. Use a recursive CTE to show an employee → manager hierarchy.
13) Keys & Constraints (Deep Dive)
What are Keys?
Keys are special columns (or group of columns) used to identify, link, or enforce
uniqueness in a table.
Types of Keys
1. Primary Key (PK)
o Uniquely identifies each row.
o Cannot be NULL.
o Only one per table.
CREATE TABLE students (
student_id INT PRIMARY KEY,
full_name VARCHAR(50) NOT NULL
);
2. Foreign Key (FK)
o Creates a link between two tables.
o References a primary key in another table.
o Enforces referential integrity.
CREATE TABLE classes (
class_id INT PRIMARY KEY,
class_name VARCHAR(20)
);
CREATE TABLE students (
student_id INT PRIMARY KEY,
full_name VARCHAR(50),
class_id INT,
FOREIGN KEY (class_id) REFERENCES classes(class_id)
);
Ensures that a student can only belong to an existing class.
3. Unique Key
o Ensures all values in a column are unique.
o Unlike PK, can be NULL.
o A table can have multiple unique keys.
CREATE TABLE users (
user_id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE,
phone VARCHAR(15) UNIQUE
);
4. Composite Key
o A primary key made of multiple columns.
o Used when a single column can’t uniquely identify rows.
CREATE TABLE enrollments (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id)
);
Same student can enroll in multiple courses, but not the same course twice.
5. Candidate Key
o Any column that could be a primary key.
o Example: In users table → user_id, email, and phone could all uniquely identify
a user.
6. Alternate Key
o Candidate keys that are not chosen as the primary key.
7. Super Key
o A set of one or more columns that uniquely identify a row.
o Primary key is a minimal super key.
What are Constraints?
Constraints enforce rules on data in a table.
Types of Constraints with Examples
1. NOT NULL → Column cannot have NULL.
CREATE TABLE students (
student_id INT PRIMARY KEY,
full_name VARCHAR(50) NOT NULL
);
2. DEFAULT → Assigns a default value if none is provided.
CREATE TABLE orders (
order_id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'Pending'
);
3. CHECK → Ensures values meet a condition.
CREATE TABLE students (
student_id INT PRIMARY KEY,
age INT CHECK (age >= 5)
);
4. UNIQUE → Ensures no duplicate values.
CREATE TABLE teachers (
teacher_id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
5. FOREIGN KEY → Links tables.
CREATE TABLE enrollments (
student_id INT,
course_id INT,
FOREIGN KEY (student_id) REFERENCES students(student_id),
FOREIGN KEY (course_id) REFERENCES courses(course_id)
);
Altering Constraints (DDL)
• Add a primary key:
• ALTER TABLE students ADD PRIMARY KEY (student_id);
• Add a foreign key:
• ALTER TABLE students
• ADD CONSTRAINT fk_class FOREIGN KEY (class_id) REFERENCES classes(class_id);
• Drop a constraint:
• ALTER TABLE students DROP PRIMARY KEY;
• ALTER TABLE students DROP FOREIGN KEY fk_class;
Quick Real World Example
Imagine a School Database:
• classes: class_id (PK), class_name
• students: student_id (PK), full_name, class_id (FK)
• enrollments: (student_id + course_id as Composite PK)
• users: user_id (PK), email (Unique)
Constraints ensure:
• No student without a valid class.
• No duplicate emails.
• No invalid enrollment (student & course must exist).
Practice for You
1. Create a teachers table with:
o teacher_id as PK
o email as UNIQUE
o salary with CHECK(salary > 3000)
2. Create an attendance table with:
o student_id referencing students
o class_id referencing classes
o Composite key (student_id, class_id)
3. Try to insert wrong data (like student with non-existing class) → see how constraint
blocks it.
14) Views in SQL
What is a View?
A view is a virtual table created from a query.
It does not store data itself — it shows data stored in real tables.
Think of it as a saved query that you can use like a table.
Syntax
-- Create a view
CREATE VIEW view_name AS
SELECT columns
FROM table
WHERE condition;
-- Use the view
SELECT * FROM view_name;
-- Update a view
CREATE OR REPLACE VIEW view_name AS
SELECT ...
FROM ...;
-- Delete a view
DROP VIEW view_name;
Real World Example
Example (School Database)
Suppose we have a students table:
student_id | full_name | class_id | score
-----------+--------------+----------+------
1 | Amit Sharma | 101 | 85
2 | Anita Singh | 102 | 92
3 | Arun Mehta | 101 | 76
4 | Bhavna Patel | 103 | 88
5 | Chetan Das | 102 | 67
Create a View for High Scorers
CREATE VIEW high_scorers AS
SELECT full_name, score
FROM students
WHERE score > 80;
Now you can simply run:
SELECT * FROM high_scorers;
Output:
full_name score
Amit Sharma 85
Anita Singh 92
Bhavna Patel 88
Create a View with Join
Suppose we have another table classes:
class_id | class_name
---------+-----------
101 | Class A
102 | Class B
103 | Class C
View showing student names with their class:
CREATE VIEW student_class_info AS
SELECT s.full_name, s.score, c.class_name
FROM students s
JOIN classes c ON s.class_id = c.class_id;
Now query it like a table:
SELECT * FROM student_class_info;
Output:
full_name score class_name
Amit Sharma 85 Class A
Anita Singh 92 Class B
Arun Mehta 76 Class A
Bhavna Patel 88 Class C
Chetan Das 67 Class B
Update a View
CREATE OR REPLACE VIEW high_scorers AS
SELECT full_name, score, class_id
FROM students
WHERE score > 85;
Drop a View
DROP VIEW high_scorers;
Why Use Views?
Simplify complex queries (like saving them as shortcuts).
Provide security (hide some columns, e.g., salary).
Make data more readable (show names instead of IDs).
Allow business reports without exposing raw tables.
Rules of Views
• You can SELECT from views like normal tables.
• Some views can be updatable (INSERT/UPDATE/DELETE allowed) if based on a single
table without aggregates.
• Views based on joins or aggregates are usually read-only.
Practice for You
1. Create a view class_avg_score → show each class with average score.
2. Create a view student_details → show student name, score, and class name (using
join).
3. Create a view top_students → show only students with score > 90.
4. Drop one of your views.
15) Stored Procedures & Functions
Stored Procedure
A stored procedure is a saved block of SQL statements that you can run by calling it.
Useful for repeated tasks (like inserting, updating, or reports).
Syntax (Create Procedure)
DELIMITER $$
CREATE PROCEDURE procedure_name (parameters)
BEGIN
-- SQL statements
END$$
DELIMITER ;
Run procedure:
CALL procedure_name(arguments);
Example 1: Procedure Without Parameter
Create a procedure to show all students.
DELIMITER $$
CREATE PROCEDURE get_all_students()
BEGIN
SELECT * FROM students;
END$$
DELIMITER ;
-- Call it
CALL get_all_students();
Example 2: Procedure With Input Parameter
Show students above a given score.
DELIMITER $$
CREATE PROCEDURE get_high_scorers(IN min_score INT)
BEGIN
SELECT full_name, score
FROM students
WHERE score > min_score;
END$$
DELIMITER ;
-- Call it
CALL get_high_scorers(80);
Example 3: Procedure With Output Parameter
Get total number of students.
DELIMITER $$
CREATE PROCEDURE count_students(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM students;
END$$
DELIMITER ;
-- Call it
CALL count_students(@total);
SELECT @total;
Stored Function
A function is similar to a procedure but must return a single value.
Functions can be used inside SQL queries.
Syntax (Create Function)
DELIMITER $$
CREATE FUNCTION function_name(parameters)
RETURNS datatype
DETERMINISTIC
BEGIN
-- SQL logic
RETURN value;
END$$
DELIMITER ;
Example 1: Function to Get Grade
DELIMITER $$
CREATE FUNCTION get_grade(score INT)
RETURNS VARCHAR(2)
DETERMINISTIC
BEGIN
DECLARE grade VARCHAR(2);
IF score >= 90 THEN
SET grade = 'A';
ELSEIF score >= 75 THEN
SET grade = 'B';
ELSEIF score >= 60 THEN
SET grade = 'C';
ELSE
SET grade = 'D';
END IF;
RETURN grade;
END$$
DELIMITER ;
-- Use function in SELECT
SELECT full_name, score, get_grade(score) AS grade
FROM students;
Output:
full_name score grade
Amit Sharma 85 B
Anita Singh 92 A
Arun Mehta 76 B
Example 2: Function to Calculate Bonus
DELIMITER $$
CREATE FUNCTION calc_bonus(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
RETURN salary * 0.10;
END$$
DELIMITER ;
-- Use inside query
SELECT emp_name, salary, calc_bonus(salary) AS bonus
FROM employees;
Difference Between Procedure & Function
Feature Procedure Function
Optional (can return OUT
Returns value Must return a single value
params)
Use in SQL
No Yes (inside SELECT)
queries
Parameters IN, OUT, INOUT Only IN
Feature Procedure Function
Simple calculations, return one
Usage Complex tasks, multiple queries
result
Practice for You
1. Write a procedure get_students_by_class(classId) → returns all students in a class.
2. Write a procedure add_student(name, classId, score) → inserts new student.
3. Write a function get_result(score) → return "Pass" if ≥40 else "Fail".
4. Use your function in a SELECT query.
What is a Stored Procedure?
A stored procedure is a named block of SQL (with optional logic/variables) saved on the
server. You CALL it later—like pressing a button that runs a mini-program close to the data.
Think: “Enroll a student safely in one go.”
Key pieces: parameters (IN, OUT, INOUT), variables, flow control (IF/WHILE), error handling
(SIGNAL/RESIGNAL + HANDLER), transactions, result sets via SELECT. MySQL Developer
Zone+1
1) Quick start: create & run your first procedure
Change delimiter (only in the mysql client)
The DELIMITER command is a client convenience so your multi-statement body isn’t cut off
by ;. We switch to $$ (or //) while defining, then switch back. MySQL Developer Zone+1
DELIMITER $$
CREATE PROCEDURE hello_school()
BEGIN
SELECT 'Hello from the School DB!' AS msg;
END $$
DELIMITER ;
CALL hello_school(); -- runs it
2) Anatomy of CREATE PROCEDURE
DELIMITER $$
CREATE PROCEDURE enroll_student
IN p_student_id INT,
IN p_class_id INT,
OUT p_status VARCHAR(50)
SQL SECURITY DEFINER
COMMENT 'Enrolls a student if seats are available'
BEGIN
-- variables
DECLARE v_seats_left INT;
-- error handler: rollback on any error
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET p_status = 'ERROR';
END;
START TRANSACTION;
-- check capacity
SELECT c.capacity - COUNT(e.student_id)
INTO v_seats_left
FROM classes c
LEFT JOIN enrollments e ON e.class_id = c.id
WHERE c.id = p_class_id
GROUP BY c.capacity;
IF v_seats_left IS NULL THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Class not found';
END IF;
IF v_seats_left <= 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'No seats available';
END IF;
INSERT INTO enrollments(student_id, class_id, enrolled_on)
VALUES (p_student_id, p_class_id, NOW());
COMMIT;
SET p_status = 'ENROLLED';
END $$
DELIMITER ;
Notes
• Parameter modes: IN, OUT, INOUT.
• Characteristics you may add: DETERMINISTIC/NOT DETERMINISTIC, CONTAINS SQL |
READS SQL DATA | MODIFIES SQL DATA, SQL SECURITY {DEFINER|INVOKER},
COMMENT.
• Use START TRANSACTION ... COMMIT/ROLLBACK inside stored programs (don’t
confuse with BEGIN ... END block syntax). MySQL Developer ZoneOracle Docs
Run & read OUT parameter
SET @status := NULL;
CALL enroll_student(101, 5, @status);
SELECT @status; -- ENROLLED or ERROR
Error handling
• DECLARE ... HANDLER to react to errors; SIGNAL to raise your own; RESIGNAL to
rethrow from a handler. MySQL Developer ZoneOracle Docs
3) Common patterns (real-world school examples)
A) Read-only reporting proc (returns a result set)
DELIMITER $$
CREATE PROCEDURE top_students(IN p_limit INT)
BEGIN
SELECT s.id, s.name, AVG(g.score) AS avg_score
FROM students s
JOIN grades g ON g.student_id = s.id
GROUP BY s.id, s.name
ORDER BY avg_score DESC
LIMIT p_limit;
END $$
DELIMITER ;
CALL top_students(10);
B) Maintenance proc with a cursor (advanced)
Use a cursor to iterate row-by-row when set-based SQL isn’t enough. (Prefer set-based
queries when possible.)
DELIMITER $$
CREATE PROCEDURE recompute_attendance_flags()
BEGIN
DECLARE v_sid INT;
DECLARE done BOOL DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT id FROM students;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_sid;
IF done THEN LEAVE read_loop; END IF;
-- Example computation per student:
UPDATE students
SET good_attendance =
(SELECT AVG(present) >= 0.9 FROM attendance WHERE student_id = v_sid)
WHERE id = v_sid;
END LOOP;
CLOSE cur;
END $$
DELIMITER ;
Cursors & NOT FOUND handler are the standard pattern. Database Administrators Stack
Exchange
C) Dynamic SQL (when table/column/ORDER BY changes at runtime)
DELIMITER $$
CREATE PROCEDURE list_by(IN p_col VARCHAR(64))
BEGIN
SET @sql = CONCAT('SELECT id, name, ', QUOTE_IDENTIFIER(p_col),' FROM students');
EXECUTE IMMEDIATE @sql; -- MySQL 8.0+ convenience
END $$
DELIMITER ;
Dynamic SQL can be done with EXECUTE IMMEDIATE or PREPARE/EXECUTE.
TutorialsPointDatabase Administrators Stack Exchange
4) Save / Load / List / Inspect
Save your procedures to a file
• In Workbench: File → “Save Script”.
• In CLI, just put the CREATE PROCEDURE statements (with DELIMITER) into
routines.sql.
• To export from a live DB: mysqldump --routines --no-data school_db > routines.sql.
Stack Overflow
Load from a file
• Non-interactive: mysql -u user -p school_db < routines.sql
• Inside mysql client: SOURCE /path/routines.sql (or \. /path/routines.sql). MySQL
Developer Zone
List & inspect procedures
SHOW PROCEDURE STATUS WHERE Db='school_db'; -- list
SHOW CREATE PROCEDURE school_db.enroll_student; -- see full definition
(Or query INFORMATION_SCHEMA.ROUTINES.) ScaleGridlogikdev.com
Update / Replace / Drop
• Easiest: DROP PROCEDURE IF EXISTS enroll_student; then CREATE PROCEDURE ...
again.
• MySQL 8.0.29+ supports CREATE OR REPLACE PROCEDURE (check your version).
MySQL Developer Zone
5) Permissions & security
• To create: you (or your role) need CREATE ROUTINE on the database.
• The creator automatically gets ALTER ROUTINE and EXECUTE unless
automatic_sp_privileges is disabled. MySQL Developer Zone
• To let others run it:
• GRANT EXECUTE ON PROCEDURE school_db.enroll_student TO 'appuser'@'%';
• -- Or at DB level:
• GRANT EXECUTE ON school_db.* TO 'appuser'@'%';
Use SQL SECURITY DEFINER (runs with definer’s rights) or INVOKER (caller’s rights) as
appropriate. MySQL Developer Zone
6) Best practices checklist
• Name clearly: sp_enroll_student, sp_top_students.
• Idempotent deploys: DROP PROCEDURE IF EXISTS ... before create.
• Validate inputs & SIGNAL helpful messages; wrap changes in transactions with a
SQLEXCEPTION handler. MySQL Developer ZoneOracle Docs
• Keep heavy business rules near the data when they must be consistent across apps.
• Prefer set-based SQL; use cursors only when truly needed. Database Administrators
Stack Exchange
• Watch transaction semantics: inside stored programs, use START TRANSACTION, not
BEGIN (which starts a block, not a transaction). Oracle Docs
7) When to use (and not use)
Great use cases
• One-click, all-or-nothing operations (enrollment, transfer, order checkout).
• Security: expose EXECUTE on procs instead of table access.
• Batch/maintenance jobs that run on the server (nightly rollups).
Think twice
• Simple CRUD that your app/ORM already does well.
• Highly dynamic logic that changes every release (may be easier in application code).
• Cross-DB portability concerns (procedural SQL differs across vendors).
8) Mini “toolbox” you’ll reuse
• Template
• DELIMITER $$
• DROP PROCEDURE IF EXISTS your_proc $$
• CREATE PROCEDURE your_proc(IN p_arg INT, OUT p_msg VARCHAR(100))
• SQL SECURITY INVOKER
• BEGIN
• DECLARE EXIT HANDLER FOR SQLEXCEPTION
• BEGIN ROLLBACK; SET p_msg='ERROR'; END;
•
• START TRANSACTION;
• -- ...work...
• COMMIT; SET p_msg='OK';
• END $$
• DELIMITER ;
• List
• SHOW PROCEDURE STATUS WHERE Db=DATABASE();
• See definition
• SHOW CREATE PROCEDURE db.proc_name;
• Grant run access
• GRANT EXECUTE ON PROCEDURE db.proc_name TO 'user'@'%';
• FLUSH PRIVILEGES;
References (for deeper reading)
• CREATE PROCEDURE syntax, characteristics, security & privileges. MySQL Developer
Zone
• Delimiters in the mysql client. MySQL Developer Zone+1
• Error handling: DECLARE ... HANDLER, SIGNAL, RESIGNAL. MySQL Developer
ZoneOracle Docs
• Cursors in stored programs. Database Administrators Stack Exchange
• Export/import scripts & SOURCE command. Stack OverflowMySQL Developer Zone
• Transactions inside stored programs (START TRANSACTION). Oracle Docs
• CREATE OR REPLACE PROCEDURE availability (8.0.29+). MySQL Developer Zone
Let’s make stored procedure examples using your employee and customer tables. I’ll cover:
• Creating stored procedures for your schema
• Running them with CALL
• Using IN, OUT, INOUT parameters
• When and why you would use them
1) Create a simple stored procedure (list employees)
USE carshowroom;
DELIMITER $$
CREATE PROCEDURE sp_get_all_employees()
BEGIN
SELECT EmpID, EmpName, Gender, Designation, Salary
FROM employee;
END $$
DELIMITER ;
-- Run it:
CALL sp_get_all_employees();
This returns all employees with their details.
2) Stored procedure with IN parameter (search customer by name)
DELIMITER $$
CREATE PROCEDURE sp_get_customer_by_name(IN p_name VARCHAR(50))
BEGIN
SELECT CustId, CustName, CustAdd, Phone, Email
FROM customer
WHERE CustName LIKE CONCAT('%', p_name, '%');
END $$
DELIMITER ;
-- Run:
CALL sp_get_customer_by_name('John');
Finds all customers whose names contain John.
3) Stored procedure with OUT parameter (count employees)
DELIMITER $$
CREATE PROCEDURE sp_count_employees(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM employee;
END $$
DELIMITER ;
-- Run:
CALL sp_count_employees(@emp_count);
SELECT @emp_count AS TotalEmployees;
Stores the total employee count into a variable.
4) Stored procedure with INOUT parameter (increase salary)
DELIMITER $$
CREATE PROCEDURE sp_increase_salary(IN p_empid INT, INOUT p_amount DECIMAL(10,2))
BEGIN
UPDATE employee
SET Salary = Salary + p_amount
WHERE EmpID = p_empid;
-- Return updated salary in the same variable
SELECT Salary INTO p_amount
FROM employee WHERE EmpID = p_empid;
END $$
DELIMITER ;
-- Run:
SET @sal := 5000; -- increase by 5000
CALL sp_increase_salary(101, @sal);
SELECT @sal AS UpdatedSalary;
Increases salary of employee 101 by 5000 and shows new salary.
5) Real-world Example (Insert new customer safely)
DELIMITER $$
CREATE PROCEDURE sp_add_customer(
IN p_name VARCHAR(50),
IN p_address VARCHAR(100),
IN p_phone VARCHAR(15),
IN p_email VARCHAR(50)
BEGIN
IF EXISTS (SELECT 1 FROM customer WHERE Email = p_email) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Customer with this email already exists!';
ELSE
INSERT INTO customer (CustName, CustAdd, Phone, Email)
VALUES (p_name, p_address, p_phone, p_email);
END IF;
END $$
DELIMITER ;
-- Run:
CALL sp_add_customer('Alice', 'New York', '9876543210', '[email protected]');
Prevents duplicate customers with the same email.
6) List, Drop & Reload Procedures
-- List all procedures in current DB
SHOW PROCEDURE STATUS WHERE Db = 'carshowroom';
-- See definition
SHOW CREATE PROCEDURE sp_get_all_employees;
-- Drop
DROP PROCEDURE IF EXISTS sp_get_all_employees;
When to use stored procedures in your car showroom project:
• Auto-generate reports (e.g., "Top 5 highest-paid employees").
• Enforce rules (e.g., "Don’t allow two customers with same email").
• Security (give users access to run EXECUTE on procedures instead of tables).
• Batch updates (e.g., yearly increment of all employees’ salary).
Stored Procedures for carshowroom
1) Show all employees
DELIMITER $$
CREATE PROCEDURE sp_get_all_employees()
BEGIN
SELECT EmpID, EmpName, Gender, Designation, Salary
FROM employee;
END $$
DELIMITER ;
2) Show all customers
DELIMITER $$
CREATE PROCEDURE sp_get_all_customers()
BEGIN
SELECT CustId, CustName, CustAdd, Phone, Email
FROM customer;
END $$
DELIMITER ;
3) Search employee by designation
DELIMITER $$
CREATE PROCEDURE sp_get_employee_by_designation(IN p_designation VARCHAR(50))
BEGIN
SELECT EmpID, EmpName, Salary
FROM employee
WHERE Designation = p_designation;
END $$
DELIMITER ;
4) Add a new employee
DELIMITER $$
CREATE PROCEDURE sp_add_employee(
IN p_name VARCHAR(50),
IN p_gender VARCHAR(10),
IN p_dob DATE,
IN p_doj DATE,
IN p_designation VARCHAR(50),
IN p_salary DECIMAL(10,2)
BEGIN
INSERT INTO employee (EmpName, Gender, DOB, DOJ, Designation, Salary)
VALUES (p_name, p_gender, p_dob, p_doj, p_designation, p_salary);
END $$
DELIMITER ;
5) Add a new customer (with duplicate email check)
DELIMITER $$
CREATE PROCEDURE sp_add_customer(
IN p_name VARCHAR(50),
IN p_address VARCHAR(100),
IN p_phone VARCHAR(15),
IN p_email VARCHAR(50)
BEGIN
IF EXISTS (SELECT 1 FROM customer WHERE Email = p_email) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Customer with this email already exists!';
ELSE
INSERT INTO customer (CustName, CustAdd, Phone, Email)
VALUES (p_name, p_address, p_phone, p_email);
END IF;
END $$
DELIMITER ;
6) Update employee salary
DELIMITER $$
CREATE PROCEDURE sp_update_salary(
IN p_empid INT,
IN p_new_salary DECIMAL(10,2)
BEGIN
UPDATE employee
SET Salary = p_new_salary
WHERE EmpID = p_empid;
END $$
DELIMITER ;
7) Delete a customer
DELIMITER $$
CREATE PROCEDURE sp_delete_customer(IN p_custid INT)
BEGIN
DELETE FROM customer WHERE CustId = p_custid;
END $$
DELIMITER ;
8) Count total employees
DELIMITER $$
CREATE PROCEDURE sp_count_employees(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM employee;
END $$
DELIMITER ;
9) Find highest-paid employee
DELIMITER $$
CREATE PROCEDURE sp_highest_paid_employee()
BEGIN
SELECT EmpID, EmpName, Designation, Salary
FROM employee
ORDER BY Salary DESC
LIMIT 1;
END $$
DELIMITER ;
10) Get customer details by phone number
DELIMITER $$
CREATE PROCEDURE sp_get_customer_by_phone(IN p_phone VARCHAR(15))
BEGIN
SELECT CustId, CustName, CustAdd, Email
FROM customer
WHERE Phone = p_phone;
END $$
DELIMITER ;
How to Use
Example runs:
CALL sp_get_all_employees();
CALL sp_get_customer_by_phone('9876543210');
CALL sp_update_salary(101, 65000);
CALL sp_add_customer('Alice', 'Delhi', '9991112222', '[email protected]');
CALL sp_count_employees(@total);
SELECT @total AS TotalEmployees;
With these 10 stored procedures, you now have:
• Data viewing (list/search).
• Data insertion (employees/customers).
• Data update (salary).
• Data delete (customer).
• Reports (highest paid employee, count of employees).
Car Showroom Stored Procedure Project Flow
We’ll organize it into modules:
1. Employee Management
o Add employee
o View all employees
o Search by designation
o Update salary
o Highest paid employee
o Count total employees
2. Customer Management
o Add customer (with duplicate email check)
o View all customers
o Search by phone number
o Delete customer
Employee Management
a) Add Employee
CALL sp_add_employee('Raj Kumar', 'Male', '1990-05-12', '2020-01-10', 'Manager', 55000);
b) View All Employees
CALL sp_get_all_employees();
c) Search Employee by Designation
CALL sp_get_employee_by_designation('Manager');
d) Update Employee Salary
CALL sp_update_salary(101, 65000);
e) Show Highest Paid Employee
CALL sp_highest_paid_employee();
f) Count Employees
CALL sp_count_employees(@total);
SELECT @total AS TotalEmployees;
Customer Management
a) Add Customer
CALL sp_add_customer('Anita Sharma', 'Delhi', '9998887776', '[email protected]');
b) View All Customers
CALL sp_get_all_customers();
c) Search Customer by Phone Number
CALL sp_get_customer_by_phone('9998887776');
d) Delete Customer
CALL sp_delete_customer(105);
Example “Menu-Driven Flow” Simulation
You can simulate a menu in your MySQL practice by running the right procedure step by
step:
---- MENU ----
1. Add New Employee
2. View Employees
3. Search Employee by Designation
4. Update Employee Salary
5. Show Highest Paid Employee
6. Count Employees
7. Add New Customer
8. View Customers
9. Search Customer by Phone
10. Delete Customer
----------------
Based on the option, you CALL the respective procedure.
For example:
• Choose 1 → CALL sp_add_employee(...)
• Choose 8 → CALL sp_get_all_customers()
Extra Challenge (Optional)
You can also create a master procedure called sp_menu_demo() that just shows the menu
with a SELECT:
DELIMITER $$
CREATE PROCEDURE sp_menu_demo()
BEGIN
SELECT '1. Add New Employee' AS Menu UNION
SELECT '2. View Employees' UNION
SELECT '3. Search Employee by Designation' UNION
SELECT '4. Update Employee Salary' UNION
SELECT '5. Show Highest Paid Employee' UNION
SELECT '6. Count Employees' UNION
SELECT '7. Add New Customer' UNION
SELECT '8. View Customers' UNION
SELECT '9. Search Customer by Phone' UNION
SELECT '10. Delete Customer';
END $$
DELIMITER ;
-- Run:
CALL sp_menu_demo();
This gives you a structured project flow using only stored procedures, perfect for
practicing SQL logic with your carshowroom DB.
What is a Transaction?
A transaction is a group of SQL statements that either:
• All succeed (COMMIT)
• Or if one fails → rollback everything (ROLLBACK)
Think of it like buying a car in your carshowroom DB:
• Decrease car stock
• Record customer purchase
• Generate invoice
All 3 must succeed together. If one fails → rollback.
TCL Commands in MySQL
1) START TRANSACTION / BEGIN
Starts a new transaction.
START TRANSACTION;
-- or simply
BEGIN;
2) COMMIT
Saves all changes made in the transaction.
COMMIT;
3) ROLLBACK
Undo all changes since last START TRANSACTION.
ROLLBACK;
4) SAVEPOINT
Sets a checkpoint inside a transaction to roll back partially.
SAVEPOINT savepoint_name;
5) ROLLBACK TO SAVEPOINT
Undo only up to a specific savepoint.
ROLLBACK TO savepoint_name;
6) RELEASE SAVEPOINT
Delete a savepoint (optional cleanup).
RELEASE SAVEPOINT savepoint_name;
7) SET AUTOCOMMIT
By default MySQL commits automatically. Disable it to control transactions manually.
SET AUTOCOMMIT = 0; -- manual commit
SET AUTOCOMMIT = 1; -- default (auto commit ON)
Real-World Example in carshowroom
Scenario: Customer buys a car → record in multiple tables
START TRANSACTION;
-- 1) Add customer
INSERT INTO customer (CustName, CustAdd, Phone, Email)
VALUES ('Ravi Sharma', 'Delhi', '9876543210', '[email protected]');
-- 2) Record employee who handled the sale (example empID=101)
UPDATE employee
SET Salary = Salary + 2000 -- commission bonus
WHERE EmpID = 101;
-- 3) Deduct from stock (assume another table cars)
UPDATE cars
SET Quantity = Quantity - 1
WHERE CarID = 5;
-- If all successful
COMMIT;
-- If any error happens
ROLLBACK;
Example with SAVEPOINT
START TRANSACTION;
UPDATE employee SET Salary = Salary + 5000 WHERE EmpID = 101;
SAVEPOINT sp1;
UPDATE employee SET Salary = Salary + 7000 WHERE EmpID = 102;
SAVEPOINT sp2;
-- Oops, we only want first update
ROLLBACK TO sp1;
COMMIT;
Only EmpID 101’s salary is updated, EmpID 102’s update is undone.
Why Transactions are Important?
• Banking: money must be deducted & credited together.
• E-commerce: stock must reduce only if order is confirmed.
• Car showroom: sale record, invoice, and stock update must succeed together.
With TCL, your database stays consistent and avoids half-done operations.
Perfect! Let’s now design a stored procedure with a transaction + error handling for your
carshowroom DB.
We’ll assume you have these tables:
• employee (EmpID, EmpName, Gender, DOB, DOJ, Designation, Salary)
• customer (CustId, CustName, CustAdd, Phone, Email)
• cars (CarID, CarName, Price, Quantity)
• sales (SaleID, CustId, EmpID, CarID, SaleDate, Amount) ← we’ll use this for
transactions.
Stored Procedure: sp_complete_car_sale
This procedure will:
1. Check if the car is in stock.
2. Insert a new sale record.
3. Deduct 1 from cars.Quantity.
4. Give the employee a commission bonus.
5. Rollback if any step fails.
Procedure Code
USE carshowroom;
DELIMITER $$
CREATE PROCEDURE sp_complete_car_sale(
IN p_custid INT,
IN p_empid INT,
IN p_carid INT
BEGIN
DECLARE v_price DECIMAL(10,2);
DECLARE v_stock INT;
-- Error handler: rollback if something goes wrong
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'Transaction Failed! Rolled back.' AS Status;
END;
START TRANSACTION;
-- 1. Check stock
SELECT Price, Quantity INTO v_price, v_stock
FROM cars
WHERE CarID = p_carid;
IF v_stock <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Car out of stock!';
END IF;
-- 2. Insert into sales table
INSERT INTO sales (CustId, EmpID, CarID, SaleDate, Amount)
VALUES (p_custid, p_empid, p_carid, NOW(), v_price);
-- 3. Deduct from stock
UPDATE cars
SET Quantity = Quantity - 1
WHERE CarID = p_carid;
-- 4. Commission bonus for employee
UPDATE employee
SET Salary = Salary + (v_price * 0.05) -- 5% commission
WHERE EmpID = p_empid;
-- If all good, commit
COMMIT;
SELECT 'Transaction Successful! Sale Completed.' AS Status;
END $$
DELIMITER ;
Run Example
-- Customer 201 buys Car 5, handled by Employee 101
CALL sp_complete_car_sale(201, 101, 5);
If stock is available →
• Sale recorded
• Car stock reduced
• Employee gets commission
If any step fails →
• Everything is rolled back
• No half-complete data
Why This is Useful
• Ensures data consistency across multiple tables.
• Avoids situations like “sale recorded but stock not updated.”
• Handles errors gracefully with rollback + message.
This procedure is like a real-world transaction in your car showroom.
What is a Trigger?
A trigger is a block of SQL code that runs automatically when an event occurs in a table:
• BEFORE INSERT / UPDATE / DELETE
• AFTER INSERT / UPDATE / DELETE
Think of it as a security camera in your car showroom: the moment an employee,
customer, or sale is added/changed/deleted → the trigger fires.
Syntax of CREATE TRIGGER
CREATE TRIGGER trigger_name
timing event ON table_name
FOR EACH ROW
BEGIN
-- SQL statements here
END;
• timing: BEFORE or AFTER
• event: INSERT, UPDATE, or DELETE
• FOR EACH ROW: runs for every row affected
Real World Examples (Car Showroom DB)
1) Log Salary Changes (Audit Employee Table)
If an employee’s salary is updated, we want to store the old + new salary in a log table.
-- Create log table first
CREATE TABLE salary_audit (
AuditID INT AUTO_INCREMENT PRIMARY KEY,
EmpID INT,
OldSalary DECIMAL(10,2),
NewSalary DECIMAL(10,2),
ChangeDate DATETIME
);
-- Trigger
DELIMITER $$
CREATE TRIGGER trg_salary_update
AFTER UPDATE ON employee
FOR EACH ROW
BEGIN
IF OLD.Salary <> NEW.Salary THEN
INSERT INTO salary_audit (EmpID, OldSalary, NewSalary, ChangeDate)
VALUES (OLD.EmpID, OLD.Salary, NEW.Salary, NOW());
END IF;
END $$
DELIMITER ;
Whenever salary changes:
UPDATE employee SET Salary = 60000 WHERE EmpID = 101;
A record is inserted into salary_audit.
2) Prevent Negative Car Stock
We don’t want cars to have stock < 0.
DELIMITER $$
CREATE TRIGGER trg_prevent_negative_stock
BEFORE UPDATE ON cars
FOR EACH ROW
BEGIN
IF NEW.Quantity < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Error: Stock cannot be negative!';
END IF;
END $$
DELIMITER ;
If you try:
UPDATE cars SET Quantity = -5 WHERE CarID = 1;
MySQL will block it.
3) Auto-Fill Customer Email if Empty
If a new customer is inserted without an email, assign a default one.
DELIMITER $$
CREATE TRIGGER trg_default_email
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
IF NEW.Email IS NULL OR NEW.Email = '' THEN
SET NEW.Email = CONCAT(NEW.CustName, '@carshowroom.com');
END IF;
END $$
DELIMITER ;
Insert:
INSERT INTO customer (CustName, CustAdd, Phone)
VALUES ('Ravi Kumar', 'Delhi', '9876543210');
When to Use Triggers
Enforcing rules (no negative stock, salary limits)
Logging changes (audits, history tracking)
Automating defaults (auto email, timestamps)
Security (block dangerous deletes)
Don’t overuse triggers → they can make debugging harder if business logic is hidden
inside them. Use for critical integrity + automation.
1) Drop a Trigger
If you no longer need a trigger, you can remove it.
DROP TRIGGER [IF EXISTS] trigger_name;
Example:
DROP TRIGGER IF EXISTS trg_salary_update;
This deletes the trigger trg_salary_update.
2) Disable / Enable a Trigger
Unlike SQL Server or Oracle, MySQL does NOT support direct DISABLE/ENABLE
commands.
But we have workarounds:
Workaround A → Drop and Recreate
• Drop the trigger when you want it disabled.
• Recreate it when you want it enabled.
Workaround B → Add a Control Condition
You can add a condition inside the trigger to make it “inactive.”
Example:
DELIMITER $$
CREATE TRIGGER trg_salary_update
AFTER UPDATE ON employee
FOR EACH ROW
BEGIN
-- Disable using a condition
IF (SELECT SettingValue FROM system_settings WHERE SettingKey = 'EnableSalaryAudit') =
'Yes' THEN
INSERT INTO salary_audit (EmpID, OldSalary, NewSalary, ChangeDate)
VALUES (OLD.EmpID, OLD.Salary, NEW.Salary, NOW());
END IF;
END $$
DELIMITER ;
Here, you can control trigger activity by changing a value in system_settings.
3) View All Triggers
To see all triggers in your database:
SHOW TRIGGERS;
Example Output:
Trigger Event Table Timing Statement
trg_salary_update UPDATE employee AFTER INSERT INTO salary_audit ...
trg_prevent_negative_stock UPDATE cars BEFORE SIGNAL SQLSTATE '45000'...
Or, for detailed info:
SELECT * FROM information_schema.triggers
WHERE trigger_schema = 'carshowroom';
4) Rename a Trigger
MySQL does not allow renaming directly.
You must:
1. Drop the old trigger
2. Create a new one with the new name
Summary
• DROP TRIGGER → remove a trigger
• SHOW TRIGGERS → view triggers
• No direct DISABLE/ENABLE, but you can simulate it with a control condition
• To rename → drop + recreate
1) Salary Audit Trigger (Track Employee Salary Changes)
Keeps a history of salary updates in a log table.
-- Log table
CREATE TABLE salary_audit (
AuditID INT AUTO_INCREMENT PRIMARY KEY,
EmpID INT,
OldSalary DECIMAL(10,2),
NewSalary DECIMAL(10,2),
ChangeDate DATETIME
);
DELIMITER $$
CREATE TRIGGER trg_salary_update
AFTER UPDATE ON employee
FOR EACH ROW
BEGIN
IF OLD.Salary <> NEW.Salary THEN
INSERT INTO salary_audit (EmpID, OldSalary, NewSalary, ChangeDate)
VALUES (OLD.EmpID, OLD.Salary, NEW.Salary, NOW());
END IF;
END $$
DELIMITER ;
2) Prevent Negative Stock Trigger (Cars Table)
Makes sure no one can reduce stock below 0.
DELIMITER $$
CREATE TRIGGER trg_prevent_negative_stock
BEFORE UPDATE ON cars
FOR EACH ROW
BEGIN
IF NEW.Quantity < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Error: Stock cannot be negative!';
END IF;
END $$
DELIMITER ;
3) Default Customer Email Trigger
If a customer is added without an email, assign a default one.
DELIMITER $$
CREATE TRIGGER trg_default_email
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
IF NEW.Email IS NULL OR NEW.Email = '' THEN
SET NEW.Email = CONCAT(NEW.CustName, '@carshowroom.com');
END IF;
END $$
DELIMITER ;
4) Track Deleted Employees (Soft Delete Audit)
If an employee is deleted, keep their info in a separate log table.
-- Log table
CREATE TABLE deleted_employees (
LogID INT AUTO_INCREMENT PRIMARY KEY,
EmpID INT,
EmpName VARCHAR(50),
Designation VARCHAR(50),
DeletedOn DATETIME
);
DELIMITER $$
CREATE TRIGGER trg_employee_delete
AFTER DELETE ON employee
FOR EACH ROW
BEGIN
INSERT INTO deleted_employees (EmpID, EmpName, Designation, DeletedOn)
VALUES (OLD.EmpID, OLD.EmpName, OLD.Designation, NOW());
END $$
DELIMITER ;
5) Auto Timestamp Sales Trigger
Automatically sets SaleDate when a sale is inserted.
DELIMITER $$
CREATE TRIGGER trg_sales_date
BEFORE INSERT ON sales
FOR EACH ROW
BEGIN
IF NEW.SaleDate IS NULL THEN
SET NEW.SaleDate = NOW();
END IF;
END $$
DELIMITER ;
These 5 triggers cover:
• Audit logging (salary + employee delete)
• Data safety (no negative stock)
• Automation (default email, auto sale date)
Index in SQL
An index is like the index of a book .
Instead of searching every page (row), MySQL jumps directly to where the data is stored.
Without index = Full table scan (slow).
With index = Faster lookups.
Types of Indexes in MySQL
1) Single Column Index
Creates an index on one column.
CREATE INDEX idx_emp_name
ON employee (EmpName);
Now queries like:
SELECT * FROM employee WHERE EmpName = 'Amit';
will run faster.
2) Unique Index
Ensures values are unique (like email, phone).
CREATE UNIQUE INDEX idx_cust_email
ON customer (Email);
Prevents duplicate emails.
3) Composite Index (Multi-column)
Index on two or more columns.
CREATE INDEX idx_cust_name_phone
ON customer (CustName, Phone);
Useful when you frequently search with both fields together.
4) Primary Key Index
When you define a PRIMARY KEY, MySQL automatically creates a unique index.
CREATE TABLE cars (
CarID INT PRIMARY KEY,
CarName VARCHAR(50),
Price DECIMAL(10,2)
);
5) Full-Text Index
Special index for text searching (MySQL MyISAM / InnoDB).
CREATE FULLTEXT INDEX idx_cust_address
ON customer (CustAdd);
Helps in searching inside text like Google search:
SELECT * FROM customer WHERE MATCH(CustAdd) AGAINST('Delhi');
Managing Indexes
-- Show all indexes of a table
SHOW INDEXES FROM employee;
-- Drop index
DROP INDEX idx_emp_name ON employee;
EXPLAIN Command
Used to analyze query execution → shows if MySQL is using an index or scanning the whole
table.
Syntax:
EXPLAIN SELECT * FROM employee WHERE EmpName = 'Amit';
Output Example:
id | select_type | table | type | possible_keys | key | rows | Extra
1 | SIMPLE | employee | ref | idx_emp_name | idx_emp_name | 1 | Using where
• key → shows which index is used.
• rows → how many rows MySQL will scan.
• type →
o ALL = full table scan (bad )
o ref / eq_ref = using index (good )
Real World Example (Car Showroom)
Suppose you often search:
SELECT * FROM sales
WHERE CustId = 101 AND SaleDate BETWEEN '2024-01-01' AND '2024-12-31';
Speed it up:
CREATE INDEX idx_sales_cust_date
ON sales (CustId, SaleDate);
Check with EXPLAIN:
EXPLAIN SELECT * FROM sales
WHERE CustId = 101 AND SaleDate BETWEEN '2024-01-01' AND '2024-12-31';
MySQL will now use the index and return results much faster.
Summary
• Indexes = speed up SELECT queries.
• Types → Single, Unique, Composite, Full-Text, Primary Key.
• EXPLAIN = check if your query is using an index.
• Use indexes wisely (too many = slows down INSERT/UPDATE).
1) Without Index (Bad Query)
Suppose we want all sales by customer CustId = 101.
EXPLAIN SELECT * FROM sales WHERE CustId = 101;
Example Output:
id | select_type | table | type | possible_keys | key | rows | Extra
1 | SIMPLE | sales | ALL | NULL | NULL | 10000 | Using where
• ALL = full table scan (MySQL looks at all rows ).
• rows = 10000 = checks every row.
This is slow if sales table is large.
2) With Index (Good Query)
Let’s create an index on CustId:
CREATE INDEX idx_sales_custid
ON sales (CustId);
Now run the same query again:
EXPLAIN SELECT * FROM sales WHERE CustId = 101;
Example Output:
id | select_type | table | type | possible_keys | key | rows | Extra
1 | SIMPLE | sales | ref | idx_sales_custid | idx_sales_custid | 5 | Using where
Improvements:
• ref → query used the index (fast).
• rows = 5 → instead of scanning 10,000 rows, MySQL jumps directly to 5 matches.
3) Range Query with Dates
Suppose we want all sales for a customer in 2024:
EXPLAIN SELECT * FROM sales
WHERE CustId = 101 AND SaleDate BETWEEN '2024-01-01' AND '2024-12-31';
Without index → ALL (full table scan).
With composite index:
CREATE INDEX idx_sales_cust_date
ON sales (CustId, SaleDate);
Now MySQL only looks at relevant rows.
4) Bad vs Good Query Example (Employee Search)
Bad query (no index, uses LIKE with % in front):
EXPLAIN SELECT * FROM employee WHERE EmpName LIKE '%mit';
Output → ALL (index cannot be used).
Good query (index works if % is not at the start):
EXPLAIN SELECT * FROM employee WHERE EmpName LIKE 'Amit%';
Output → uses idx_emp_name.
Key Takeaways
• Always use EXPLAIN to check query performance.
• ALL = bad (full table scan).
• ref / eq_ref / const = good (using index).
• Use composite indexes for common filters (like CustId + Date).
• Avoid %word searches (index won’t help).
Why Indexing Matters
Indexes = fast SELECT but slower INSERT/UPDATE/DELETE (because MySQL must update
indexes too).
Rule: Only add indexes where they will really speed up searches, filters, or joins.
Recommended Indexes for carshowroom
1) Employee Table
CREATE INDEX idx_emp_name ON employee (EmpName);
CREATE INDEX idx_emp_designation ON employee (Designation);
• Speeds up searches by name or designation.
• Useful for HR queries like:
SELECT * FROM employee WHERE Designation = 'Manager';
2) Customer Table
CREATE UNIQUE INDEX idx_cust_email ON customer (Email);
CREATE INDEX idx_cust_name ON customer (CustName);
• Email must be unique (prevents duplicates).
• CustName helps when searching by name.
• Useful for queries like:
SELECT * FROM customer WHERE CustName LIKE 'Ravi%';
3) Cars Table
CREATE INDEX idx_car_name ON cars (CarName);
CREATE INDEX idx_car_price ON cars (Price);
• Useful when customers filter cars by name or price range:
SELECT * FROM cars WHERE Price BETWEEN 500000 AND 800000;
4) Sales Table
This is the most important table for reporting.
CREATE INDEX idx_sales_custid ON sales (CustId);
CREATE INDEX idx_sales_empid ON sales (EmpID);
CREATE INDEX idx_sales_carid ON sales (CarID);
CREATE INDEX idx_sales_date ON sales (SaleDate);
CREATE INDEX idx_sales_cust_date ON sales (CustId, SaleDate); -- composite
• Why so many? Because sales will often be filtered by:
o Customer → “Show me all sales by CustId 101”
o Employee → “Sales handled by EmpID 105”
o Car → “How many times was CarID 7 sold?”
o Date → “Sales in Jan 2024”
o Customer + Date → “Sales for CustId 101 in 2024”
Composite index (CustId, SaleDate) makes date range searches per customer super fast.
5) Full-Text Search (Optional)
If you want search like Google on addresses:
CREATE FULLTEXT INDEX idx_cust_address ON customer (CustAdd);
Then you can run:
SELECT * FROM customer WHERE MATCH(CustAdd) AGAINST('Delhi');
6) How to Check Index Usage
SHOW INDEXES FROM sales;
EXPLAIN SELECT * FROM sales WHERE CustId = 101 AND SaleDate > '2024-01-01';
• If key column in EXPLAIN shows your index → it’s working.
Indexing Best Practices
1. Don’t index every column → slows down inserts/updates.
2. Index columns used frequently in WHERE, JOIN, ORDER BY, GROUP BY.
3. Use composite indexes for common multi-column searches.
4. Keep index names meaningful (idx_sales_cust_date not idx1).
With these indexes, your car showroom DB will handle large data smoothly.
What is Normalization?
Normalization = process of organizing data into tables so that:
• Data is not duplicated unnecessarily
• Data integrity is maintained
• Queries run faster
• Database is easy to maintain
It is done using Normal Forms (NF): 1NF → 2NF → 3NF (sometimes BCNF).
Normal Forms Explained with Real World Example (Car Showroom)
Bad Design (Unnormalized Table)
Imagine you store all data in one table:
SalesTable
---------------------------------------------------------------
SaleID | CustName | CustAdd | Phone | CarName | Price | EmpName | SaleDate
Problems:
• Customer details repeated for every sale.
• Car price stored multiple times.
• Hard to update (if customer changes phone → must update everywhere).
Step 1: First Normal Form (1NF)
Rule: Remove repeating groups → make atomic columns.
We separate customer, cars, employee, and sales:
Customer Table
CustId | CustName | CustAdd | Phone | Email
Employee Table
EmpId | EmpName | Gender | DOB | DOJ | Designation | Salary
Cars Table
CarId | CarName | Price | Quantity
Sales Table
SaleId | CustId | EmpId | CarId | SaleDate | Amount
Now no repeating groups → 1NF.
Step 2: Second Normal Form (2NF)
Rule: Remove partial dependency (applies to composite keys).
Example: If SaleId + CarId was the key, but CarName depends only on CarId, not on the full
key → move it to cars table.
Already solved in our design → 2NF.
Step 3: Third Normal Form (3NF)
Rule: Remove transitive dependency (non-key depending on another non-key).
Example: If we stored EmpDeptName in employee table (but dept name depends on DeptID,
not EmpID), we should separate it into a Department table.
So we can extend:
Department Table
DeptId | DeptName
Employee Table (updated)
EmpId | EmpName | Gender | DOB | DOJ | Designation | Salary | DeptId
Now → 3NF.
Database Design Commands (Creating Normalized Tables)
-- Customer
CREATE TABLE customer (
CustId INT PRIMARY KEY AUTO_INCREMENT,
CustName VARCHAR(50),
CustAdd VARCHAR(100),
Phone VARCHAR(15),
Email VARCHAR(50) UNIQUE
);
-- Employee
CREATE TABLE employee (
EmpId INT PRIMARY KEY AUTO_INCREMENT,
EmpName VARCHAR(50),
Gender VARCHAR(10),
DOB DATE,
DOJ DATE,
Designation VARCHAR(30),
Salary DECIMAL(10,2),
DeptId INT
);
-- Department
CREATE TABLE department (
DeptId INT PRIMARY KEY AUTO_INCREMENT,
DeptName VARCHAR(50)
);
-- Cars
CREATE TABLE cars (
CarId INT PRIMARY KEY AUTO_INCREMENT,
CarName VARCHAR(50),
Price DECIMAL(10,2),
Quantity INT
);
-- Sales
CREATE TABLE sales (
SaleId INT PRIMARY KEY AUTO_INCREMENT,
CustId INT,
EmpId INT,
CarId INT,
SaleDate DATE,
Amount DECIMAL(10,2),
FOREIGN KEY (CustId) REFERENCES customer(CustId),
FOREIGN KEY (EmpId) REFERENCES employee(EmpId),
FOREIGN KEY (CarId) REFERENCES cars(CarId)
);
Benefits of Normalization
No duplicate customer/employee/car info
Easy updates (change once, reflected everywhere)
Data consistency
Efficient queries
When to Denormalize
Sometimes, for speed, we break rules and duplicate data (e.g., store CarName in sales for
faster reporting).
Normalization = clean design
Denormalization = faster reporting (but risk duplication)
Summary
• 1NF → Atomic values, no repeating groups
• 2NF → Remove partial dependency
• 3NF → Remove transitive dependency
• Use foreign keys to link tables
• Result = clean, professional DB design
A mini-project is where you bring together DDL, DML, SELECT, functions, joins, subqueries,
views, procedures, triggers, TCL, indexes, normalization → all in one.
This will make you industry-ready with SQL.
Mini-Project: Car Showroom Management System
We’ll design and implement a database for your carshowroom with real-world requirements.
Step 1: Create Database
CREATE DATABASE carshowroom;
USE carshowroom;
Step 2: Create Tables (DDL)
-- Customer Table
CREATE TABLE customer (
CustId INT PRIMARY KEY AUTO_INCREMENT,
CustName VARCHAR(50),
CustAdd VARCHAR(100),
Phone VARCHAR(15),
Email VARCHAR(50) UNIQUE
);
-- Department Table
CREATE TABLE department (
DeptId INT PRIMARY KEY AUTO_INCREMENT,
DeptName VARCHAR(50)
);
-- Employee Table
CREATE TABLE employee (
EmpId INT PRIMARY KEY AUTO_INCREMENT,
EmpName VARCHAR(50),
Gender VARCHAR(10),
DOB DATE,
DOJ DATE,
Designation VARCHAR(30),
Salary DECIMAL(10,2),
DeptId INT,
FOREIGN KEY (DeptId) REFERENCES department(DeptId)
);
-- Cars Table
CREATE TABLE cars (
CarId INT PRIMARY KEY AUTO_INCREMENT,
CarName VARCHAR(50),
Price DECIMAL(10,2),
Quantity INT
);
-- Sales Table
CREATE TABLE sales (
SaleId INT PRIMARY KEY AUTO_INCREMENT,
CustId INT,
EmpId INT,
CarId INT,
SaleDate DATE,
Amount DECIMAL(10,2),
FOREIGN KEY (CustId) REFERENCES customer(CustId),
FOREIGN KEY (EmpId) REFERENCES employee(EmpId),
FOREIGN KEY (CarId) REFERENCES cars(CarId)
);
Step 3: Insert Sample Data (DML)
-- Customers
INSERT INTO customer (CustName, CustAdd, Phone, Email)
VALUES
('Ravi Kumar', 'Delhi', '9876543210', '[email protected]'),
('Sneha Sharma', 'Mumbai', '9876500000', '[email protected]');
-- Department
INSERT INTO department (DeptName) VALUES ('Sales'), ('Service');
-- Employees
INSERT INTO employee (EmpName, Gender, DOB, DOJ, Designation, Salary, DeptId)
VALUES
('Amit Verma', 'Male', '1990-05-12', '2020-01-15', 'Sales Executive', 35000, 1),
('Pooja Singh', 'Female', '1995-08-25', '2021-03-10', 'Service Advisor', 28000, 2);
-- Cars
INSERT INTO cars (CarName, Price, Quantity)
VALUES
('Honda City', 1200000, 5),
('Maruti Swift', 800000, 10);
-- Sales
INSERT INTO sales (CustId, EmpId, CarId, SaleDate, Amount)
VALUES
(1, 1, 1, '2023-12-15', 1200000),
(2, 1, 2, '2023-12-20', 800000);
Step 4: Queries (SELECT)
1. Get all sales details with customer and car info
SELECT s.SaleId, c.CustName, car.CarName, e.EmpName, s.Amount, s.SaleDate
FROM sales s
JOIN customer c ON s.CustId = c.CustId
JOIN cars car ON s.CarId = car.CarId
JOIN employee e ON s.EmpId = e.EmpId;
2. Find total sales by employee
SELECT e.EmpName, SUM(s.Amount) AS TotalSales
FROM sales s
JOIN employee e ON s.EmpId = e.EmpId
GROUP BY e.EmpName;
3. Find cars with low stock
SELECT CarName, Quantity
FROM cars
WHERE Quantity < 5;
Step 5: Views
CREATE VIEW sales_summary AS
SELECT s.SaleId, c.CustName, car.CarName, e.EmpName, s.Amount, s.SaleDate
FROM sales s
JOIN customer c ON s.CustId = c.CustId
JOIN cars car ON s.CarId = car.CarId
JOIN employee e ON s.EmpId = e.EmpId;
Step 6: Stored Procedure
DELIMITER //
CREATE PROCEDURE GetCustomerSales(IN custId INT)
BEGIN
SELECT s.SaleId, car.CarName, s.Amount, s.SaleDate
FROM sales s
JOIN cars car ON s.CarId = car.CarId
WHERE s.CustId = custId;
END //
DELIMITER ;
-- Run it
CALL GetCustomerSales(1);
Step 7: Trigger
DELIMITER //
CREATE TRIGGER reduce_stock
AFTER INSERT ON sales
FOR EACH ROW
BEGIN
UPDATE cars
SET Quantity = Quantity - 1
WHERE CarId = NEW.CarId;
END //
DELIMITER ;
Step 8: Transaction (TCL)
START TRANSACTION;
INSERT INTO sales (CustId, EmpId, CarId, SaleDate, Amount)
VALUES (1, 1, 2, CURDATE(), 800000);
UPDATE cars SET Quantity = Quantity - 1 WHERE CarId = 2;
COMMIT; -- or ROLLBACK if something goes wrong
Step 9: Index & Explain
-- Index on customer email for fast search
CREATE INDEX idx_email ON customer(Email);
-- Analyze query execution
EXPLAIN SELECT * FROM sales WHERE CustId = 1;
What You Learned from Mini-Project
• Designed a normalized database
• Used DDL, DML, SELECT, Joins, Group By, Views, Procedures, Triggers, Transactions,
Indexes
• Built a real-world car showroom system