Complete academic management system with many-to-many relationships.
Design a database for a university system. Consider:
Tasks:
| Relationship | Type | Implementation | Description |
|---|---|---|---|
| Departments ←→ Students | One-to-Many | STUDENTS.department_id FK | A department has many students, but each student belongs to one major department. Crow's Foot: ||--o{ |
| Departments ←→ Professors | One-to-Many | PROFESSORS.department_id FK | A department employs many professors, but each professor primarily belongs to one department. Crow's Foot: ||--o{ |
| Departments ←→ Courses | One-to-Many | COURSES.department_id FK | A department offers many courses, but each course belongs to one department. Crow's Foot: ||--o{ |
| Students ←→ Courses | Many-to-Many | ENROLLMENTS junction table with semester, year, grade, enrollment_date, status | A student enrolls in many courses, and a course has many students. Unique constraint: (student_id, course_id, semester, year). Crow's Foot: }o--o{ |
| Professors ←→ Courses | Many-to-Many | TEACHING junction table with semester, year, section_number, schedule, classroom | A professor teaches many courses, and a course can be taught by multiple professors (different sections). Unique constraint: (professor_id, course_id, semester, year, section_number). Crow's Foot: }o--o{ |
Attributes:
Attributes:
Attributes:
Attributes:
Attributes:
Attributes:
-- ============================================
-- UNIVERSITY DATABASE SCHEMA
-- ============================================
-- 1. DEPARTMENTS Table
CREATE TABLE DEPARTMENTS (
department_id INT AUTO_INCREMENT PRIMARY KEY,
department_name VARCHAR(100) UNIQUE NOT NULL,
building VARCHAR(100),
phone VARCHAR(20),
budget INT,
INDEX idx_department_name (department_name)
) ENGINE=InnoDB;
-- 2. STUDENTS Table
CREATE TABLE STUDENTS (
student_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
enrollment_date DATE NOT NULL,
department_id INT,
major VARCHAR(100),
year INT CHECK (year BETWEEN 1 AND 4),
gpa DECIMAL(3,2) CHECK (gpa BETWEEN 0.00 AND 4.00),
status VARCHAR(20) DEFAULT 'Active',
CHECK (status IN ('Active', 'Graduated', 'Suspended', 'Withdrawn')),
FOREIGN KEY (department_id) REFERENCES DEPARTMENTS(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE,
INDEX idx_email (email),
INDEX idx_department (department_id),
INDEX idx_status (status),
INDEX idx_name (last_name, first_name)
) ENGINE=InnoDB;
-- 3. PROFESSORS Table
CREATE TABLE PROFESSORS (
professor_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
department_id INT,
office VARCHAR(50),
phone VARCHAR(20),
hire_date DATE,
rank VARCHAR(50),
CHECK (rank IN ('Assistant Professor', 'Associate Professor', 'Full Professor', 'Lecturer', 'Adjunct')),
FOREIGN KEY (department_id) REFERENCES DEPARTMENTS(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE,
INDEX idx_email (email),
INDEX idx_department (department_id),
INDEX idx_name (last_name, first_name)
) ENGINE=InnoDB;
-- 4. COURSES Table
CREATE TABLE COURSES (
course_id INT AUTO_INCREMENT PRIMARY KEY,
course_code VARCHAR(20) UNIQUE NOT NULL,
course_name VARCHAR(255) NOT NULL,
credits INT NOT NULL,
department_id INT,
description TEXT,
max_enrollment INT DEFAULT 30,
CHECK (credits BETWEEN 1 AND 6),
CHECK (max_enrollment > 0),
FOREIGN KEY (department_id) REFERENCES DEPARTMENTS(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE,
INDEX idx_course_code (course_code),
INDEX idx_department (department_id),
INDEX idx_credits (credits)
) ENGINE=InnoDB;
-- 5. ENROLLMENTS Junction Table
CREATE TABLE ENROLLMENTS (
enrollment_id INT AUTO_INCREMENT PRIMARY KEY,
student_id INT NOT NULL,
course_id INT NOT NULL,
semester VARCHAR(20) NOT NULL,
year INT NOT NULL,
grade VARCHAR(2),
enrollment_date DATE DEFAULT (CURRENT_DATE),
status VARCHAR(20) DEFAULT 'Enrolled',
CHECK (semester IN ('Fall', 'Spring', 'Summer')),
CHECK (year >= 2000 AND year <= 2100),
CHECK (status IN ('Enrolled', 'Dropped', 'Completed')),
CHECK (grade IN ('A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'F', 'I', 'W', 'P')),
UNIQUE KEY unique_enrollment (student_id, course_id, semester, year),
FOREIGN KEY (student_id) REFERENCES STUDENTS(student_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (course_id) REFERENCES COURSES(course_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
INDEX idx_student (student_id),
INDEX idx_course (course_id),
INDEX idx_semester_year (semester, year),
INDEX idx_status (status)
) ENGINE=InnoDB;
-- 6. TEACHING Junction Table
CREATE TABLE TEACHING (
teaching_id INT AUTO_INCREMENT PRIMARY KEY,
professor_id INT NOT NULL,
course_id INT NOT NULL,
semester VARCHAR(20) NOT NULL,
year INT NOT NULL,
section_number INT DEFAULT 1,
schedule VARCHAR(100),
classroom VARCHAR(50),
CHECK (semester IN ('Fall', 'Spring', 'Summer')),
CHECK (year >= 2000 AND year <= 2100),
CHECK (section_number > 0),
UNIQUE KEY unique_teaching (professor_id, course_id, semester, year, section_number),
FOREIGN KEY (professor_id) REFERENCES PROFESSORS(professor_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (course_id) REFERENCES COURSES(course_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
INDEX idx_professor (professor_id),
INDEX idx_course (course_id),
INDEX idx_semester_year (semester, year)
) ENGINE=InnoDB;
-- ============================================
-- SAMPLE DATA
-- ============================================
-- Insert departments
INSERT INTO DEPARTMENTS (department_name, building, phone, budget) VALUES
('Computer Science', 'Science Hall', '555-1000', 2000000),
('Mathematics', 'Math Building', '555-1001', 1500000),
('Physics', 'Science Hall', '555-1002', 1800000),
('Business', 'Business Center', '555-1003', 2500000);
-- Insert professors
INSERT INTO PROFESSORS (first_name, last_name, email, department_id, office, phone, hire_date, rank) VALUES
('John', 'Smith', 'j.smith@university.edu', 1, 'SH-301', '555-2001', '2010-08-15', 'Full Professor'),
('Jane', 'Doe', 'j.doe@university.edu', 1, 'SH-302', '555-2002', '2015-01-10', 'Associate Professor'),
('Robert', 'Johnson', 'r.johnson@university.edu', 2, 'MB-201', '555-2003', '2012-09-01', 'Full Professor');
-- Insert courses
INSERT INTO COURSES (course_code, course_name, credits, department_id, description, max_enrollment) VALUES
('CS101', 'Introduction to Programming', 3, 1, 'Basic programming concepts', 40),
('CS201', 'Data Structures', 3, 1, 'Advanced data structures and algorithms', 35),
('CS301', 'Database Systems', 3, 1, 'Database design and SQL', 30),
('MATH101', 'Calculus I', 4, 2, 'Differential calculus', 50),
('MATH201', 'Linear Algebra', 3, 2, 'Matrices and vector spaces', 35);
-- Insert students
INSERT INTO STUDENTS (first_name, last_name, email, enrollment_date, department_id, major, year, gpa, status) VALUES
('Alice', 'Williams', 'alice.w@student.edu', '2023-08-25', 1, 'Computer Science', 2, 3.75, 'Active'),
('Bob', 'Brown', 'bob.b@student.edu', '2023-08-25', 1, 'Computer Science', 2, 3.50, 'Active'),
('Charlie', 'Davis', 'charlie.d@student.edu', '2022-08-20', 2, 'Mathematics', 3, 3.90, 'Active');
-- Insert teaching assignments (Fall 2025)
INSERT INTO TEACHING (professor_id, course_id, semester, year, section_number, schedule, classroom) VALUES
(1, 1, 'Fall', 2025, 1, 'MWF 09:00-10:00', 'SH-101'),
(1, 3, 'Fall', 2025, 1, 'TTh 14:00-15:30', 'SH-105'),
(2, 2, 'Fall', 2025, 1, 'MWF 10:00-11:00', 'SH-102'),
(3, 4, 'Fall', 2025, 1, 'TTh 09:00-10:30', 'MB-101');
-- Insert enrollments (Fall 2025)
INSERT INTO ENROLLMENTS (student_id, course_id, semester, year, grade, status) VALUES
(1, 1, 'Fall', 2025, NULL, 'Enrolled'), -- Alice in CS101
(1, 2, 'Fall', 2025, NULL, 'Enrolled'), -- Alice in CS201
(2, 1, 'Fall', 2025, NULL, 'Enrolled'), -- Bob in CS101
(2, 3, 'Fall', 2025, NULL, 'Enrolled'), -- Bob in CS301
(3, 4, 'Fall', 2025, NULL, 'Enrolled'); -- Charlie in MATH101
-- Student's current schedule
SELECT
c.course_code,
c.course_name,
c.credits,
CONCAT(p.first_name, ' ', p.last_name) AS professor,
t.schedule,
t.classroom
FROM ENROLLMENTS e
JOIN COURSES c ON e.course_id = c.course_id
JOIN TEACHING t ON c.course_id = t.course_id
AND e.semester = t.semester
AND e.year = t.year
JOIN PROFESSORS p ON t.professor_id = p.professor_id
WHERE e.student_id = 1
AND e.semester = 'Fall'
AND e.year = 2025
AND e.status = 'Enrolled';
-- Professor's teaching load
SELECT
c.course_code,
c.course_name,
t.section_number,
t.schedule,
t.classroom,
COUNT(e.enrollment_id) AS enrolled_students
FROM TEACHING t
JOIN COURSES c ON t.course_id = c.course_id
LEFT JOIN ENROLLMENTS e ON t.course_id = e.course_id
AND t.semester = e.semester
AND t.year = e.year
AND e.status = 'Enrolled'
WHERE t.professor_id = 1
AND t.semester = 'Fall'
AND t.year = 2025
GROUP BY t.teaching_id;
-- Department statistics
SELECT
d.department_name,
COUNT(DISTINCT s.student_id) AS total_students,
COUNT(DISTINCT p.professor_id) AS total_professors,
COUNT(DISTINCT c.course_id) AS total_courses
FROM DEPARTMENTS d
LEFT JOIN STUDENTS s ON d.department_id = s.department_id AND s.status = 'Active'
LEFT JOIN PROFESSORS p ON d.department_id = p.department_id
LEFT JOIN COURSES c ON d.department_id = c.department_id
GROUP BY d.department_id, d.department_name;
-- Student transcript
SELECT
e.semester,
e.year,
c.course_code,
c.course_name,
c.credits,
e.grade
FROM ENROLLMENTS e
JOIN COURSES c ON e.course_id = c.course_id
WHERE e.student_id = 1
AND e.status = 'Completed'
ORDER BY e.year DESC, e.semester;