You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ensure data integrity in a multi-user environment.
π§βπ« Lesson 1: Advanced Stored Procedures
Stored Procedure with Parameters
-- Create stored procedure to add a new student
DELIMITER //
CREATE PROCEDURE sp_AddStudent(
IN p_first_name VARCHAR(50),
IN p_last_name VARCHAR(50),
IN p_email VARCHAR(100),
IN p_date_of_birth DATE,
IN p_gender CHAR(1),
OUT p_student_id INT
)
BEGININSERT INTO Students(first_name, last_name, email, date_of_birth, gender)
VALUES(p_first_name, p_last_name, p_email, p_date_of_birth, p_gender);
SET p_student_id = LAST_INSERT_ID();
END //
DELIMITER ;
-- Call stored procedure
CALL sp_AddStudent('Hoang', 'Tran', 'hoang.tran@example.com', '2001-08-15', 'M', @new_id);
SELECT @new_id AS new_student_id;
Error Handling in Stored Procedure
DELIMITER //
CREATE PROCEDURE sp_EnrollStudent(
IN p_student_id INT,
IN p_course_id INT
)
BEGIN
DECLARE exit_handler BOOLEAN DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGINSET exit_handler = TRUE;
ROLLBACK;
SELECT'Error occurred during course enrollment'AS error_message;
END;
START TRANSACTION;
-- Check if student exists
IF NOT EXISTS (SELECT1FROM Students WHERE student_id = p_student_id) THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Student does not exist';
END IF;
-- Check if course exists
IF NOT EXISTS (SELECT1FROM Courses WHERE course_id = p_course_id) THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Course does not exist';
END IF;
-- Check if student already enrolled in this course
IF EXISTS (SELECT1FROM Enrollments WHERE student_id = p_student_id AND course_id = p_course_id) THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Student already enrolled in this course';
END IF;
-- Add new enrollmentINSERT INTO Enrollments(student_id, course_id, enrollment_date)
VALUES(p_student_id, p_course_id, CURDATE());
IF exit_handler = FALSE THEN
COMMIT;
SELECT'Course enrollment successful'AS success_message;
END IF;
END //
DELIMITER ;
Using Cursor to Process Data Row by Row
DELIMITER //
CREATE PROCEDURE sp_UpdateStudentRanks()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE s_id INT;
DECLARE s_avg DECIMAL(4,2);
-- Declare cursor
DECLARE student_cursor CURSOR FOR
SELECT student_id, (math_score + physics_score + chemistry_score)/3AS avg_score
FROM Students;
-- Declare handler for cursor
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Open cursor
OPEN student_cursor;
-- Start loop
student_loop: LOOP
-- Read data row by row
FETCH student_cursor INTO s_id, s_avg;
-- Check if data is exhausted
IF done THEN
LEAVE student_loop;
END IF;
-- Update rank based on average scoreUPDATE Students SET
average_score = s_avg,
rank = CASE
WHEN s_avg >=8.0 THEN 'Excellent'
WHEN s_avg >=6.5 THEN 'Good'
WHEN s_avg >=5.0 THEN 'Average'
ELSE 'Weak'
END
WHERE student_id = s_id;
END LOOP;
-- Close cursor
CLOSE student_cursor;
END //
DELIMITER ;
Stored Procedure Returning Value
DELIMITER //CREATEFUNCTIONfn_CalculateGPA(
p_student_id INT
) RETURNS DECIMAL(4,2)
DETERMINISTIC
BEGIN
DECLARE avg_grade DECIMAL(4,2);
SELECTAVG(grade) INTO avg_grade
FROM Enrollments
WHERE student_id = p_student_id;
-- If no grade (NULL), return 0
IF avg_grade IS NULL THEN
RETURN 0.0;
ELSE
RETURN avg_grade;
END IF;
END //
DELIMITER ;
-- Use functionSELECTs.student_id,
CONCAT(s.first_name, '', s.last_name) AS full_name,
fn_CalculateGPA(s.student_id) AS gpa
FROM Students s;
π§βπ« Lesson 2: Triggers and Constraints
Trigger BEFORE INSERT
Executed before a record is inserted into the table.
DELIMITER //CREATETRIGGERbefore_student_insert
BEFORE INSERT ON Students
FOR EACH ROW
BEGIN-- Convert email to lowercaseSETNEW.email=LOWER(NEW.email);
-- Check email format
IF NEW.email NOT LIKE'%@%.%' THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Invalid email format';
END IF;
-- Check age (must be 16 or older)
IF NEW.date_of_birth> DATE_SUB(CURDATE(), INTERVAL 16 YEAR) THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Student must be 16 years or older';
END IF;
END //
DELIMITER ;
Trigger AFTER UPDATE
Executed after a record is updated.
DELIMITER //CREATETRIGGERafter_grade_update
AFTER UPDATEON Enrollments
FOR EACH ROW
BEGIN-- If grade changes, log it
IF OLD.grade<>NEW.grade THEN
INSERT INTO GradeChangeLog(
student_id,
course_id,
old_grade,
new_grade,
changed_at,
changed_by
)
VALUES(
NEW.student_id,
NEW.course_id,
OLD.grade,
NEW.grade,
NOW(),
CURRENT_USER()
);
END IF;
END //
DELIMITER ;
Trigger BEFORE DELETE
Executed before a record is deleted.
DELIMITER //CREATETRIGGERbefore_course_delete
BEFORE DELETEON Courses
FOR EACH ROW
BEGIN-- Do not allow deleting course with enrolled students
DECLARE student_count INT;
SELECTCOUNT(*) INTO student_count
FROM Enrollments
WHERE course_id =OLD.course_id;
IF student_count >0 THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Cannot delete course with enrolled students';
END IF;
END //
DELIMITER ;
Trigger to Maintain Data Integrity
DELIMITER //CREATETRIGGERafter_enrollment_insert
AFTER INSERT ON Enrollments
FOR EACH ROW
BEGIN-- Update enrolled student count in Courses tableUPDATE Courses
SET enrolled_students = (
SELECTCOUNT(*)
FROM Enrollments
WHERE course_id =NEW.course_id
)
WHERE course_id =NEW.course_id;
END //
DELIMITER ;
-- Similar for DELETE and UPDATECREATETRIGGERafter_enrollment_delete
AFTER DELETEON Enrollments
FOR EACH ROW
BEGINUPDATE Courses
SET enrolled_students = (
SELECTCOUNT(*)
FROM Enrollments
WHERE course_id =OLD.course_id
)
WHERE course_id =OLD.course_id;
END //
DELIMITER ;
Transaction Management
-- Example of transaction when transferring points from one student to anotherSTART TRANSACTION;
-- Deduct points from source studentUPDATE Students
SET bonus_points = bonus_points -10WHERE student_id =101;
-- Check for error (e.g., negative points)
IF (SELECT bonus_points FROM Students WHERE student_id =101) <0 THEN
ROLLBACK;
SELECT'Not enough points to transfer'AS message;
ELSE
-- Add points to target studentUPDATE Students
SET bonus_points = bonus_points +10WHERE student_id =102;
COMMIT;
SELECT'Points transferred successfully'AS message;
END IF;
Isolation Levels
-- READ UNCOMMITTED (lowest level, allows reading uncommitted data)SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- READ COMMITTED (only read committed data)SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- REPEATABLE READ (default level in MySQL, ensures repeatable reads)SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- SERIALIZABLE (highest level, all transactions executed sequentially)SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Example transaction with REPEATABLE READ levelSET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- Read dataSELECT*FROM Students WHERE student_id =1;
-- Perform other operations...-- Read data again, ensuring result is same as first readSELECT*FROM Students WHERE student_id =1;
COMMIT;
Handling Locks and Deadlocks
-- Set lock wait timeoutSET innodb_lock_wait_timeout =50; -- 50 seconds-- Example transaction with FOR UPDATE (creates row-level lock)START TRANSACTION;
-- Lock row for readingSELECT*FROM Enrollments WHERE enrollment_id =101 FOR UPDATE;
-- Perform updateUPDATE Enrollments SET grade =9.5WHERE enrollment_id =101;
COMMIT;
-- Handling deadlock with timeoutSTART TRANSACTION;
-- Try to lock data with timeoutSELECT*FROM Students WHERE student_id =1 FOR UPDATE NOWAIT; -- Error immediately if locked-- orSELECT*FROM Students WHERE student_id =1 FOR UPDATE WAIT 10; -- Wait max 10 seconds-- If deadlock occurs, MySQL will automatically rollback one transaction-- We can handle this in application codeCOMMIT;
π§βπ« Lesson 4: Data Security
User Management and Permissions
-- Create user with encrypted passwordCREATEUSER 'teacher_user'@'localhost' IDENTIFIED BY 'Strong_P@ssw0rd!';
-- Create role (MySQL 8.0+)
CREATE ROLE 'app_read', 'app_write', 'app_admin';
-- Grant permissions to roleGRANTSELECTON SchoolManagement.* TO 'app_read';
GRANTSELECT, INSERT, UPDATEON SchoolManagement.* TO 'app_write';
GRANT ALL PRIVILEGES ON SchoolManagement.* TO 'app_admin';
-- Grant role to userGRANT'app_write' TO 'teacher_user'@'localhost';
-- Set default roleSET DEFAULT ROLE 'app_write' TO 'teacher_user'@'localhost';
-- Grant permissions directly on specific tablesGRANTSELECTONSchoolManagement.Students TO 'student_user'@'localhost';
GRANTSELECT, UPDATE (first_name, last_name, email) ONSchoolManagement.Students
TO 'student_user'@'localhost';
-- Revoke permissionsREVOKEUPDATEONSchoolManagement.StudentsFROM'student_user'@'localhost';
Encryption and Data Security
-- Encrypt sensitive data-- 1. Use built-in encryption functionUPDATE Users SET
password_hash = SHA2(CONCAT(password, salt), 256)
WHERE user_id =101;
-- 2. Use AES for data needing decryptionSET @key ='my_secure_key';
-- EncryptUPDATE Students SET
encrypted_ssn = AES_ENCRYPT(social_security_number, @key)
WHERE student_id =1;
-- DecryptSELECT
student_id,
first_name,
CONVERT(AES_DECRYPT(encrypted_ssn, @key) USING utf8) as ssn
FROM Students;
SQL Injection Prevention
-- Unsafe way (DO NOT USE)-- PHP code: $query = "SELECT * FROM Users WHERE username = '$username' AND password = '$password'";-- Safe way using Prepared Statements-- PHP with PDO/*$stmt = $pdo->prepare("SELECT * FROM Users WHERE username = ? AND password_hash = ?");$stmt->execute([$username, hash('sha256', $password . $salt)]);*/-- Java with JDBC/*PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM Users WHERE username = ? AND password_hash = ?");pstmt.setString(1, username);pstmt.setString(2, hash("SHA-256", password + salt));ResultSet rs = pstmt.executeQuery();if (rs.next()) { // Login successful}*/-- Or use stored procedure
DELIMITER //
CREATE PROCEDURE sp_AuthenticateUser(
IN p_username VARCHAR(100),
IN p_password VARCHAR(100)
)
BEGIN
DECLARE p_salt VARCHAR(32);
-- Get user saltSELECT salt INTO p_salt FROM Users WHERE username = p_username;
-- Check authenticationSELECT user_id, username, email, role
FROM Users
WHERE username = p_username
AND password_hash = SHA2(CONCAT(p_password, p_salt), 256);
END //
DELIMITER ;
-- Call procedure to authenticate
CALL sp_AuthenticateUser('user1', 'password123');
π§βπ« Lesson 5: SQL and Web Applications
Connecting to Database from Application
// Connecting from JAVA with JDBCimportjava.sql.*;
publicclassDatabaseConnection {
privatestaticfinalStringURL = "jdbc:mysql://localhost:3306/SchoolManagement";
privatestaticfinalStringUSER = "app_user";
privatestaticfinalStringPASSWORD = "secure_password";
publicstaticvoidmain(String[] args) {
try (Connectionconn = DriverManager.getConnection(URL, USER, PASSWORD)) {
System.out.println("Connection successful!");
// Execute querytry (Statementstmt = conn.createStatement()) {
ResultSetrs = stmt.executeQuery("SELECT * FROM Students");
while (rs.next()) {
System.out.println(rs.getInt("student_id") + " - " +
rs.getString("first_name") + " " +
rs.getString("last_name"));
}
}
// Use Prepared Statement (safer)Stringquery = "SELECT * FROM Students WHERE student_id = ?";
try (PreparedStatementpstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, 1); // Set value for parameterResultSetrs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("Found: " + rs.getString("first_name"));
}
}
} catch (SQLExceptione) {
e.printStackTrace();
}
}
}
-- 1. Use INDEX for frequently searched columnsCREATEINDEXidx_students_emailON Students(email);
CREATEINDEXidx_enrollments_studentON Enrollments(student_id);
CREATEINDEXidx_enrollments_courseON Enrollments(course_id);
-- 2. Select only necessary columnsSELECT student_id, first_name, last_name FROM Students WHERE gender ='F';
-- instead of-- SELECT * FROM Students WHERE gender = 'F';-- 3. Use LIMIT for paginationSELECT*FROM Students LIMIT10 OFFSET 20; -- Page 3, 10 items/page-- 4. Use JOIN efficiently-- Instead of multiple individual queriesSELECTs.student_id,
s.first_name,
s.last_name,
c.course_name,
e.gradeFROM Students s
JOIN Enrollments e ONs.student_id=e.student_idJOIN Courses c ONe.course_id=c.course_idWHEREs.student_id=101;
-- 5. Use EXPLAIN to analyze query
EXPLAIN SELECT*FROM Students WHERE last_name LIKE'Nguy%';
Handling N+1 Problem and Performance
// N+1 Problem (should not use)List<Student> students = getStudents(); // 1 query to get student listfor (Studentstudent : students) {
List<Course> courses = getCoursesForStudent(student.getId()); // N queries// Process...
}
// Solution: use JOIN// SQL: SELECT s.*, c.* FROM Students s JOIN Enrollments e ON ... JOIN Courses c ON ...
-- Optimized query to solve N+1 problem-- Get students and enrolled courses in 1 querySELECTs.student_id,
s.first_name,
s.last_name,
JSON_ARRAYAGG(
JSON_OBJECT(
'course_id', c.course_id,
'course_name', c.course_name,
'grade', e.grade
)
) AS enrolled_courses
FROM Students s
LEFT JOIN Enrollments e ONs.student_id=e.student_idLEFT JOIN Courses c ONe.course_id=c.course_idGROUP BYs.student_id, s.first_name, s.last_name;
π§ͺ FINAL PROJECT: Library Book Borrowing Management
Problem Description
Build a library management database with tables:
Books: book information
Users: user information
Borrowings: borrowing transactions
Requirements
Design full data structure with constraints.
Create stored procedures to:
Add new book.
Register user.
Handle book borrowing (check stock, record borrow date).
Handle book return (update status, calculate fine if overdue).
Create triggers to:
Automatically update book quantity when borrowing/returning.