Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
4 views137 pages

Full SQL

Uploaded by

Sunil Singh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views137 pages

Full SQL

Uploaded by

Sunil Singh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 137

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');

Auto email = Ravi [email protected].

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

You might also like