initial commit

This commit is contained in:
SowinskiBraeden committed 2025-11-28 16:27:38 -08:00
commit 9b8a13f429
17 files changed
+1277

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
-- 1.
SELECT le_section_id, lab_set_code, course_code, la_title, lab_day_of_week
FROM lab_event
INNER JOIN lab_section
ON lab_section.lab_section_id = lab_event.le_section_id
INNER JOIN lab_assignment
ON lab_assignment.la_lab_number = lab_event.le_number
WHERE term_code = '202530';
-- 2.
SELECT pgr_student_id, COUNT(*) AS labs_attended
FROM progress
GROUP BY pgr_student_id;
-- 3.
SELECT pgr_student_id, student_set_code, lab_section_id, COUNT(*) FILTER (WHERE pgr_late 1= 'f') AS late_submissions
FROM progress
INNER JOIN student
ON student.student_id = progress.pgr_student_id
INNER JOIN lab_section
ON student.student_set_code = lab_section.lab_set_code
GROUP BY pgr_student_id, student_set_code, lab_section_id;
-- 4.
SELECT student_set_code, TO_CHAR(AVG(pgr_instructor_assessment::numeric), 'FM999999.00') AS section_average
FROM progress
INNER JOIN student
ON student.student_id = progress.pgr_student_id
GROUP BY student_set_code
ORDER BY student_set_code;
-- 5.
SELECT student_first_name || ' ' || student_last_name AS student_name, student_set_code, le_number
FROM student
INNER JOIN progress
ON progress.pgr_student_id = student.student_id
INNER JOIN lab_event
ON lab_event.le_id = progress.pgr_event_id
WHERE pgr_instructor_assessment IS NULL OR pgr_self_assessment IS NULL;
-- 6.
SELECT student_id, student_first_name || ' ' || student_last_name AS student_name, student_set_code, TO_CHAR(AVG(pgr_instructor_assessment::numeric), 'FM999999.00') AS student_average
FROM student
JOIN progress
ON student.student_id = progress.pgr_student_id
GROUP BY student_id, student_first_name, student_last_name, student_set_code
HAVING AVG(pgr_instructor_assessment::numeric) >= 4.5
ORDER BY student_id;
+56
View File
@@ -0,0 +1,56 @@
CREATE OR REPLACE VIEW ta_view AS
SELECT
ls.lab_section_id AS lab_section,
e.le_id,
s.student_id,
s.student_first_name || ' ' ||
s.student_last_name AS full_name,
p.pgr_attendance,
p.pgr_inlab_submission_link,
p.pgr_instructor_assessment
FROM progress p
JOIN student s
ON p.pgr_student_id = s.student_id
JOIN lab_event e
ON p.pgr_event_id = e.le_id
JOIN lab_section ls
ON e.le_section_id = ls.lab_section_id;
CREATE OR REPLACE VIEW v_section_overview AS
SELECT
t.term_code,
ss.set_code AS set_name,
c.course_code,
ls.lab_section_id AS section_id,
COUNT(DISTINCT le.le_id) AS total_events,
AVG(CAST(p.pgr_instructor_assessment AS NUMERIC)) AS avg_instructor_assessment
FROM lab_section AS ls
JOIN course AS c ON c.course_code = ls.lab_course_code
JOIN term AS t ON t.term_code = ls.lab_term_code
JOIN student_set AS ss ON ss.set_code = ls.lab_set_code
LEFT JOIN lab_event AS le
ON le.le_section_id = ls.lab_section_id
AND le.course_code = ls.lab_course_code
AND le.term_code = ls.lab_term_code
LEFT JOIN progress AS p
ON p.pgr_event_id = le.le_id
GROUP BY
t.term_code,
ss.set_code,
c.course_code,
ls.lab_section_id;
--everything from section
SELECT *
FROM v_section_overview
ORDER BY term_code, set_name, course_code, section_id;
-- only sections that actually have events
SELECT *
FROM v_section_overview
WHERE total_events > 0
ORDER BY total_events DESC;
--This is the view for ta
SELECT *
FROM ta_view;
+11
View File
@@ -0,0 +1,11 @@
-- 1. Create a role for TAs
CREATE ROLE ta_role;
-- 2. Allow this role to view your TA-related views
GRANT SELECT ON v_ta_progress_summary, v_section_overview TO ta_role;
-- 3. Create a demo user (only works if your server allows user creation)
CREATE USER ta_demo WITH PASSWORD 'ta_demo123';
-- 4. Give the demo user the TA role
GRANT ta_role TO ta_demo;
+44
View File
@@ -0,0 +1,44 @@
Primary Keys
For our design, we decided to go with natural primary keys for all tables because the data we were given in the CSVs already included unique identifiers that made sense in the real world.
We didnt see a reason to generate artificial IDs since each table had something that was already unique and meaningful.
Natural PKs we used:
course_code e.g. COMP1510
term_code e.g. 202530
student_set e.g. SETD
student uses student_id (like A001)
lab_section uses lab_section_id (like L01)
lab_assignment uses la_assignment_id (like LAB01)
lab_event uses le_id (like L01-L01)
user_account, progress, and progress_change_log also use text-based IDs that are unique.
Foreign Keys & Relationships
We made sure every relationship matched the business rules:
Students must belong to a Set, and the student_set_code cant be null.
If a set code changes, it cascades to all students (ON UPDATE CASCADE).
Lab Sections link to a Course, Term, and Set. All of those are required and set to cascade on update too.
Lab Events belong to a Lab Section, and we used a composite foreign key (le_section_id, course_code, term_code) to make sure the event always matches the correct section, course, and term. We also added ON DELETE CASCADE so deleting a section deletes its events automatically.
Student_Section connects students to lab sections, and if a section gets deleted, the link goes with it.
Progress connects a student to a lab event. Deleting an event now also deletes any related progress (we used ON DELETE CASCADE for that).
Progress Change Log connects to both progress and user_account. Deleting a progress record removes its log, but deleting a user is blocked to protect the audit trail.
Uniqueness & Constraints
Student emails must be unique (no duplicate addresses).
Each combination of (lab_section_id, lab_course_code, lab_term_code) is unique to prevent mix-ups between sections.
Term codes must follow the six-digit format (CHECK (term_code ~ '^[0-9]{6}$')).
Course credits must be between 1 and 9.
lab_event checks that le_ends_at is after le_starts_at.
progress checks that pgr_polished_submitted_at is after pgr_inlab_submitted_at when both are present.
Boolean fields like pgr_prepared and pgr_late default to FALSE.
Cascading Logic
We used cascading only when it made logical sense:
Deleting a lab_section removes its lab_events, which also removes any related progress and change logs automatically.
For tables like user_account, we kept delete restricted so we dont lose audit history.
Naming Conventions
We used prefixes to make joins easier and keep things organized.
For example:
le_ for lab_event columns,
pgr_ for progress,
chl_ for progress_change_log.
Also, every constraint is named properly (pk_, fk_, ck_, uq_) so if theres an error, its obvious where it came from.
+168
View File
@@ -0,0 +1,168 @@
DROP SCHEMA IF EXISTS lab_tracker_group_22 CASCADE;
CREATE SCHEMA lab_tracker_group_22;
SET search_path TO lab_tracker_group_22;
DROP TABLE IF EXISTS progress_change_log;
DROP TABLE IF EXISTS progress;
DROP TABLE IF EXISTS lab_event;
DROP TABLE IF EXISTS lab_assignment;
DROP TABLE IF EXISTS student_section;
DROP TABLE IF EXISTS lab_section;
DROP TABLE IF EXISTS student;
DROP TABLE IF EXISTS user_account;
DROP TABLE IF EXISTS student_set;
DROP TABLE IF EXISTS term;
DROP TABLE IF EXISTS course;
CREATE TABLE course (
course_code VARCHAR(20) CONSTRAINT pk_course PRIMARY KEY,
course_title VARCHAR(200),
credits NUMERIC(3,0)
CONSTRAINT ck_course_credits CHECK (credits BETWEEN 1 AND 9)
);
CREATE TABLE term (
term_code VARCHAR(10) CONSTRAINT pk_term PRIMARY KEY,
term_name VARCHAR(50),
term_start_date DATE,
term_end_date DATE,
CONSTRAINT ck_term_code_fmt CHECK (term_code ~ '^[0-9]{6}$')
);
CREATE TABLE student_set (
set_code VARCHAR(10) CONSTRAINT pk_student_set PRIMARY KEY,
set_campus VARCHAR(50)
);
CREATE TABLE user_account (
user_id VARCHAR(50) CONSTRAINT pk_user_account PRIMARY KEY,
user_full_name VARCHAR(120) NOT NULL,
user_role VARCHAR(30),
user_email VARCHAR(120) CONSTRAINT uq_user_email UNIQUE
);
CREATE TABLE student (
student_id VARCHAR(30) CONSTRAINT pk_student PRIMARY KEY,
student_set_code VARCHAR(10) NOT NULL,
student_first_name VARCHAR(80) NOT NULL,
student_last_name VARCHAR(80) NOT NULL,
student_email VARCHAR(120),
CONSTRAINT uq_student_email UNIQUE (student_email),
CONSTRAINT fk_student_set
FOREIGN KEY (student_set_code)
REFERENCES student_set(set_code)
ON UPDATE CASCADE
);
CREATE TABLE lab_section (
lab_section_id VARCHAR(20) CONSTRAINT pk_lab_section PRIMARY KEY,
lab_course_code VARCHAR(20) NOT NULL,
lab_term_code VARCHAR(10) NOT NULL,
lab_set_code VARCHAR(10) NOT NULL,
lab_section_type VARCHAR(10),
lab_day_of_week VARCHAR(10),
lab_start_time TIME,
lab_end_time TIME,
lab_location VARCHAR(100),
CONSTRAINT fk_lab_section_course FOREIGN KEY (lab_course_code)
REFERENCES course(course_code) ON UPDATE CASCADE,
CONSTRAINT fk_lab_section_term FOREIGN KEY (lab_term_code)
REFERENCES term(term_code) ON UPDATE CASCADE,
CONSTRAINT fk_lab_section_set FOREIGN KEY (lab_set_code)
REFERENCES student_set(set_code) ON UPDATE CASCADE,
CONSTRAINT uq_lab_section_triplet
UNIQUE (lab_section_id, lab_course_code, lab_term_code)
);
CREATE TABLE student_section (
ss_section_id VARCHAR(20) NOT NULL,
ss_student_id VARCHAR(30) NOT NULL,
CONSTRAINT pk_student_section PRIMARY KEY (ss_section_id, ss_student_id),
CONSTRAINT fk_student_section_section FOREIGN KEY (ss_section_id)
REFERENCES lab_section(lab_section_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_student_section_student FOREIGN KEY (ss_student_id)
REFERENCES student(student_id) ON UPDATE CASCADE
);
CREATE TABLE lab_assignment (
la_assignment_id VARCHAR(20) CONSTRAINT pk_lab_assignment PRIMARY KEY, la_course_code VARCHAR(20) NOT NULL,
la_term_code VARCHAR(10),
la_lab_number INTEGER,
la_title VARCHAR(200),
CONSTRAINT fk_assignment_course FOREIGN KEY (la_course_code)
REFERENCES course(course_code) ON UPDATE CASCADE,
CONSTRAINT fk_assignment_term FOREIGN KEY (la_term_code)
REFERENCES term(term_code) ON UPDATE CASCADE
);
CREATE TABLE lab_event (
le_id VARCHAR(32) CONSTRAINT pk_lab_event PRIMARY KEY,
le_section_id VARCHAR(20) NOT NULL,
course_code VARCHAR(20) NOT NULL,
term_code VARCHAR(10) NOT NULL,
le_number INTEGER NOT NULL,
le_starts_at TIMESTAMP NOT NULL,
le_ends_at TIMESTAMP NOT NULL,
le_due_at TIMESTAMP,
le_location VARCHAR(100),
CONSTRAINT fk_event_section_triplet
FOREIGN KEY (le_section_id, course_code, term_code)
REFERENCES lab_section (lab_section_id, lab_course_code, lab_term_code)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT ck_event_time_order CHECK (le_ends_at > le_starts_at)
);
CREATE TABLE progress (
pgr_id VARCHAR(50) CONSTRAINT pk_progress PRIMARY KEY,
pgr_student_id VARCHAR(30) NOT NULL,
pgr_event_id VARCHAR(32) NOT NULL,
pgr_lab_number INTEGER,
pgr_status VARCHAR(50),
pgr_prepared BOOLEAN NOT NULL DEFAULT FALSE,
pgr_attendance VARCHAR(20),
pgr_inlab_submitted_at TIMESTAMP,
pgr_inlab_submission_link TEXT,
pgr_polished_submitted_at TIMESTAMP,
pgr_polished_submission_link TEXT,
pgr_instructor_assessment TEXT,
pgr_self_assessment TEXT,
pgr_late BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT fk_prog_student
FOREIGN KEY (pgr_student_id)
REFERENCES student(student_id)
ON UPDATE CASCADE,
CONSTRAINT fk_prog_event
FOREIGN KEY (pgr_event_id)
REFERENCES lab_event(le_id)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT ck_prog_time_order CHECK (
pgr_polished_submitted_at IS NULL
OR pgr_inlab_submitted_at IS NULL
OR pgr_polished_submitted_at >= pgr_inlab_submitted_at
)
);
CREATE TABLE progress_change_log (
chl_change_id VARCHAR(20) CONSTRAINT pk_progress_change_log PRIMARY KEY,
chl_progress_id VARCHAR(50) NOT NULL,
chl_changed_by VARCHAR(50) NOT NULL,
chl_changed_at TIMESTAMP NOT NULL,
chl_field VARCHAR(100) NOT NULL,
chl_old_value VARCHAR(255),
chl_new_value VARCHAR(255),
chl_reason TEXT,
CONSTRAINT fk_chglog_progress FOREIGN KEY (chl_progress_id)
REFERENCES progress(pgr_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_chglog_user FOREIGN KEY (chl_changed_by)
REFERENCES user_account(user_id) ON UPDATE CASCADE
);
+186
View File
@@ -0,0 +1,186 @@
-- Clean DB before inserting
TRUNCATE TABLE progress_change_log CASCADE;
TRUNCATE TABLE progress CASCADE;
TRUNCATE TABLE lab_event CASCADE;
TRUNCATE TABLE lab_assignment CASCADE;
TRUNCATE TABLE student_section CASCADE;
TRUNCATE TABLE lab_section CASCADE;
TRUNCATE TABLE student CASCADE;
TRUNCATE TABLE user_account CASCADE;
TRUNCATE TABLE student_set CASCADE;
TRUNCATE TABLE term CASCADE;
TRUNCATE TABLE course CASCADE;
-- Insert courses
INSERT INTO course
(course_code, course_title, credits)
VALUES
('COMP2714', 'Relational Database Systems', 3);
-- Insert terms
INSERT INTO term
(term_code, term_name, term_start_date, term_end_date)
VALUES
('202510', 'Winter 2025', '2025-01-06', '2025-04-11'),
('202520', 'Spring/Summer 2025', '2025-04-28', '2025-08-08'),
('202530', 'Fall 2025', '2025-09-02', '2025-12-12');
-- Insert student sets
INSERT INTO student_set
(set_code, set_campus)
VALUES
('A', 'Burnaby'),
('B', 'Burnaby'),
('C', 'Burnaby'),
('D', 'Burnaby'),
('E', 'Downtown'),
('F', 'Downtown');
-- Insert users
INSERT INTO user_account
(user_id, user_full_name, user_role, user_email)
VALUES
('u_instructor', 'Maryam Khezrzadeh', 'instructor', 'mkhezrzadeh@bcit.ca'),
('u_ta1', 'Daniel Saavedra', 'ta', 'dsaavedra@bcit.ca'),
('u_system', 'Lab Tracker System', 'system', 'noreply@labtracker.local');
-- Insert students
INSERT INTO student
(student_id, student_set_code, student_first_name, student_last_name, student_email)
VALUES
('A001', 'A', 'Ava', 'Nguyen', 'ava.nguyen@my.bcit.ca'),
('A002', 'A', 'Noah', 'Kim', 'noah.kim@my.bcit.ca'),
('A003', 'A', 'Oliver', 'Singh', 'oliver.singh@my.bcit.ca'),
('B001', 'B', 'Maya', 'Fischer', 'maya.fischer@my.bcit.ca'),
('B002', 'B', 'Leo', 'Park', 'leo.park@my.bcit.ca'),
('B003', 'B', 'Zoé', 'Martin', 'zoe.martin@my.bcit.ca'),
('C001', 'C', 'Sofia', 'Chen', 'sofia.chen@my.bcit.ca'),
('C002', 'C', 'Arjun', 'Patel', 'arjun.patel@my.bcit.ca'),
('C003', 'C', 'Liam', 'OReilly', 'liam.oreilly@my.bcit.ca'),
('D001', 'D', 'Layla', 'Haddad', 'layla.haddad@my.bcit.ca'),
('D002', 'D', 'Ethan', 'Wong', 'ethan.wong@my.bcit.ca'),
('D003', 'D', 'Nora', 'Iverson', 'nora.iverson@my.bcit.ca'),
('E001', 'E', 'Diego', 'Alvarez', 'diego.alvarez@my.bcit.ca'),
('E002', 'E', 'Hana', 'Yamamoto', 'hana.yamamoto@my.bcit.ca'),
('E003', 'E', 'Farah', 'Rahimi', 'farah.rahimi@my.bcit.ca'),
('F001', 'F', 'Marco', 'Russo', 'marco.russo@my.bcit.ca'),
('F002', 'F', 'Amir', 'Kazemi', 'amir.kazemi@my.bcit.ca'),
('F003', 'F', 'Chloe', 'Dubois', 'chloe.dubois@my.bcit.ca');
-- Insert lab sections
INSERT INTO lab_section
(lab_section_id, lab_course_code, lab_term_code, lab_set_code, lab_section_type, lab_day_of_week, lab_start_time, lab_end_time, lab_location)
VALUES
('L01', 'COMP2714', '202530', 'A', 'LAB', 'Mon', '09:30', '11:20', 'BBY-SW01-3460'),
('L02', 'COMP2714', '202530', 'B', 'LAB', 'Mon', '13:30', '15:20', 'BBY-SW01-3465'),
('L03', 'COMP2714', '202530', 'C', 'LAB', 'Tue', '18:30', '20:20', 'BBY-SW03-2605'),
('L04', 'COMP2714', '202530', 'D', 'LAB', 'Wed', '09:30', '11:20', 'BBY-SE12-101'),
('L05', 'COMP2714', '202530', 'E', 'LAB', 'Wed', '13:30', '15:20', 'DTC-310'),
('L06', 'COMP2714', '202530', 'F', 'LAB', 'Thu', '18:30', '20:20', 'DTC-318');
-- Insert lab assignments
INSERT INTO lab_assignment
(la_assignment_id, la_course_code, la_term_code, la_lab_number, la_title)
VALUES
('LAB01', 'COMP2714', '202530', 1, 'Environment Setup & Intro SQL'),
('LAB02', 'COMP2714', '202530', 2, 'Conceptual → Logical Mapping'),
('LAB03', 'COMP2714', '202530', 3, 'Logical ERD & Constraints'),
('LAB04', 'COMP2714', '202530', 4, 'Normalization to 3NF'),
('LAB05', 'COMP2714', '202530', 5, 'DDL Implementation'),
('LAB06', 'COMP2714', '202530', 6, 'DML: INSERT/UPDATE/DELETE'),
('LAB07', 'COMP2714', '202530', 7, 'SELECT & JOIN Practice'),
('LAB08', 'COMP2714', '202530', 8, 'Views & Indexes');
-- Insert student enrollments by set → section
INSERT INTO student_section (ss_section_id, ss_student_id) VALUES
('L01', 'A001'),
('L01', 'A002'),
('L01', 'A003'),
('L02', 'B001'),
('L02', 'B002'),
('L02', 'B003'),
('L03', 'C001'),
('L03', 'C002'),
('L03', 'C003'),
('L04', 'D001'),
('L04', 'D002'),
('L04', 'D003'),
('L05', 'E001'),
('L05', 'E002'),
('L05', 'E003'),
('L06', 'F001'),
('L06', 'F002'),
('L06', 'F003');
-- Insert lab events
INSERT INTO lab_event
(le_id, le_section_id, course_code, term_code, le_number, le_starts_at, le_ends_at, le_due_at, le_location)
VALUES
('L01-L01', 'L01', 'COMP2714', '202530', 1, '2025-09-08 09:30', '2025-09-08 11:20', '2025-09-14 23:59', 'BBY-SW01-3460'),
('L01-L02', 'L01', 'COMP2714', '202530', 2, '2025-09-15 09:30', '2025-09-15 11:20', '2025-09-21 23:59', 'BBY-SW01-3460'),
('L01-L03', 'L01', 'COMP2714', '202530', 3, '2025-09-22 09:30', '2025-09-22 11:20', '2025-09-28 23:59', 'BBY-SW01-3460'),
('L02-L01', 'L02', 'COMP2714', '202530', 1, '2025-09-08 13:30', '2025-09-08 15:20', '2025-09-14 23:59', 'BBY-SW01-3465'),
('L02-L02', 'L02', 'COMP2714', '202530', 2, '2025-09-15 13:30', '2025-09-15 15:20', '2025-09-21 23:59', 'BBY-SW01-3465'),
('L02-L03', 'L02', 'COMP2714', '202530', 3, '2025-09-22 13:30', '2025-09-22 15:20', '2025-09-28 23:59', 'BBY-SW01-3465'),
('L03-L01', 'L03', 'COMP2714', '202530', 1, '2025-09-09 18:30', '2025-09-09 20:20', '2025-09-14 23:59', 'BBY-SW03-2605'),
('L03-L02', 'L03', 'COMP2714', '202530', 2, '2025-09-16 18:30', '2025-09-16 20:20', '2025-09-21 23:59', 'BBY-SW03-2605'),
('L03-L03', 'L03', 'COMP2714', '202530', 3, '2025-09-23 18:30', '2025-09-23 20:20', '2025-09-28 23:59', 'BBY-SW03-2605'),
('L04-L01', 'L04', 'COMP2714', '202530', 1, '2025-09-10 09:30', '2025-09-10 11:20', '2025-09-14 23:59', 'BBY-SE12-101'),
('L04-L02', 'L04', 'COMP2714', '202530', 2, '2025-09-17 09:30', '2025-09-17 11:20', '2025-09-21 23:59', 'BBY-SE12-101'),
('L04-L03', 'L04', 'COMP2714', '202530', 3, '2025-09-24 09:30', '2025-09-24 11:20', '2025-09-28 23:59', 'BBY-SE12-101'),
('L05-L01', 'L05', 'COMP2714', '202530', 1, '2025-09-10 13:30', '2025-09-10 15:20', '2025-09-15 09:00', 'DTC-310'),
('L05-L02', 'L05', 'COMP2714', '202530', 2, '2025-09-17 13:30', '2025-09-17 15:20', '2025-09-22 09:00', 'DTC-310'),
('L05-L03', 'L05', 'COMP2714', '202530', 3, '2025-09-24 13:30', '2025-09-24 15:20', '2025-09-29 09:00', 'DTC-310'),
('L06-L01', 'L06', 'COMP2714', '202530', 1, '2025-09-11 18:30', '2025-09-11 20:20', '2025-09-15 09:00', 'DTC-318'),
('L06-L02', 'L06', 'COMP2714', '202530', 2, '2025-09-18 18:30', '2025-09-18 20:20', '2025-09-22 09:00', 'DTC-318'),
('L06-L03', 'L06', 'COMP2714', '202530', 3, '2025-09-25 18:30', '2025-09-25 20:20', '2025-09-29 09:00', 'DTC-318');
-- Insert progress
INSERT INTO progress
(pgr_id, pgr_student_id, pgr_event_id, pgr_lab_number, pgr_status, pgr_prepared, pgr_attendance, pgr_inlab_submitted_at, pgr_inlab_submission_link, pgr_polished_submitted_at, pgr_polished_submission_link, pgr_instructor_assessment, pgr_self_assessment, pgr_late)
VALUES
('A001-L01-L01','A001', 'L01-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 10:45', 'https://submit.bcit.ca/comp2714/inlab/A001-L01-L01.pdf', '2025-09-09 12:45', 'https://submit.bcit.ca/comp2714/polished/A001-L01-L01.pdf', '8.5', '8.2', 'FALSE'),
('A001-L01-L02','A001', 'L01-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-15 10:35', 'https://submit.bcit.ca/comp2714/inlab/A001-L01-L02.pdf', '2025-09-17 11:35', 'https://submit.bcit.ca/comp2714/polished/A001-L01-L02.pdf', '7.0', '6.7', 'FALSE'),
('A002-L01-L01','A002', 'L01-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 10:45', 'https://submit.bcit.ca/comp2714/inlab/A002-L01-L01.pdf', '2025-09-09 12:45', 'https://submit.bcit.ca/comp2714/polished/A002-L01-L01.pdf', '8.5', '8.2', 'FALSE'),
('A002-L01-L02','A002', 'L01-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-15 10:45', 'https://submit.bcit.ca/comp2714/inlab/A002-L01-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('A003-L01-L01','A003', 'L01-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 10:45', 'https://submit.bcit.ca/comp2714/inlab/A003-L01-L01.pdf', '2025-09-09 12:45', 'https://submit.bcit.ca/comp2714/polished/A003-L01-L01.pdf', '8.5', '8.2', 'FALSE'),
('A003-L01-L02','A003', 'L01-L02',2, 'Submitted', 'FALSE', 'Present', '2025-09-15 10:35', 'https://submit.bcit.ca/comp2714/inlab/A003-L01-L02.pdf', '2025-09-17 11:35', 'https://submit.bcit.ca/comp2714/polished/A003-L01-L02.pdf', '7.0', '6.7', 'FALSE'),
('B001-L02-L01','B001', 'L02-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 14:45', 'https://submit.bcit.ca/comp2714/inlab/B001-L02-L01.pdf', '2025-09-09 16:45', 'https://submit.bcit.ca/comp2714/polished/B001-L02-L01.pdf', '8.5', '8.2', 'FALSE'),
('B001-L02-L02','B001', 'L02-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-15 14:35', 'https://submit.bcit.ca/comp2714/inlab/B001-L02-L02.pdf', '2025-09-17 15:35', 'https://submit.bcit.ca/comp2714/polished/B001-L02-L02.pdf', '7.0', '6.7', 'FALSE'),
('B002-L02-L01','A002', 'L02-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 14:45', 'https://submit.bcit.ca/comp2714/inlab/B002-L02-L01.pdf', '2025-09-09 16:45', 'https://submit.bcit.ca/comp2714/polished/B002-L02-L01.pdf', '8.5', '8.2', 'FALSE'),
('B002-L02-L02','A002', 'L02-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-15 14:40', 'https://submit.bcit.ca/comp2714/inlab/B002-L02-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('B003-L02-L01','B003', 'L02-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-08 14:45', 'https://submit.bcit.ca/comp2714/inlab/B003-L02-L01.pdf', '2025-09-09 16:45', 'https://submit.bcit.ca/comp2714/polished/B003-L02-L01.pdf', '8.5', '8.2', 'FALSE'),
('B003-L02-L02','B003', 'L02-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-15 14:35', 'https://submit.bcit.ca/comp2714/inlab/B003-L02-L02.pdf', '2025-09-17 15:35', 'https://submit.bcit.ca/comp2714/polished/B003-L02-L02.pdf', '7.0', '6.7', 'FALSE'),
('C001-L01-L01','C001', 'L03-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-09 19:45', 'https://submit.bcit.ca/comp2714/inlab/C001-L03-L01.pdf', '2025-09-10 21:45', 'https://submit.bcit.ca/comp2714/polished/C001-L03-L01.pdf', '8.5', '8.2', 'FALSE'),
('C001-L01-L02','C001', 'L03-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-16 19:35', 'https://submit.bcit.ca/comp2714/inlab/C001-L03-L02.pdf', '2025-09-18 20:35', 'https://submit.bcit.ca/comp2714/polished/C001-L03-L02.pdf', '7.0', '6.7', 'FALSE'),
('C002-L03-L01','C002', 'L03-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-09 19:45', 'https://submit.bcit.ca/comp2714/inlab/C002-L03-L01.pdf', '2025-09-10 21:45', 'https://submit.bcit.ca/comp2714/polished/C002-L03-L01.pdf', '8.5', '8.2', 'FALSE'),
('C002-L03-L02','C002', 'L03-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-16 19:40', 'https://submit.bcit.ca/comp2714/inlab/C002-L03-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('C003-L03-L01','C003', 'L03-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-09 19:45', 'https://submit.bcit.ca/comp2714/inlab/C003-L03-L01.pdf', '2025-09-10 21:45', 'https://submit.bcit.ca/comp2714/polished/C003-L02-L01.pdf', '8.5', '8.2', 'FALSE'),
('C003-L03-L02','C003', 'L03-L02',2, 'Submitted', 'FALSE', 'Present', '2025-09-16 19:35', 'https://submit.bcit.ca/comp2714/inlab/C003-L03-L02.pdf', '2025-09-18 20:35', 'https://submit.bcit.ca/comp2714/polished/C003-L02-L02.pdf', '7.0', '6.7', 'FALSE'),
('D001-L04-L01','D001', 'L04-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 10:45', 'https://submit.bcit.ca/comp2714/inlab/D001-L04-L01.pdf', '2025-09-11 12:45', 'https://submit.bcit.ca/comp2714/polished/D001-L04-L01.pdf', '8.5', '8.2', 'FALSE'),
('D001-L04-L02','D001', 'L04-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-17 10:35', 'https://submit.bcit.ca/comp2714/inlab/D001-L04-L02.pdf', '2025-09-19 11:35', 'https://submit.bcit.ca/comp2714/polished/D001-L04-L02.pdf', '7.0', '6.7', 'FALSE'),
('D002-L04-L01','D002', 'L04-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 10:45', 'https://submit.bcit.ca/comp2714/inlab/D002-L04-L01.pdf', '2025-09-11 12:45', 'https://submit.bcit.ca/comp2714/polished/D002-L04-L01.pdf', '8.5', '8.2', 'FALSE'),
('D002-L04-L02','D002', 'L04-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-17 10:40', 'https://submit.bcit.ca/comp2714/inlab/D002-L04-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('D003-L04-L01','D003', 'L04-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 10:45', 'https://submit.bcit.ca/comp2714/inlab/D003-L04-L01.pdf', '2025-09-11 12:45', 'https://submit.bcit.ca/comp2714/polished/D003-L04-L01.pdf', '8.5', '8.2', 'FALSE'),
('D003-L04-L02','D003', 'L04-L02',2, 'Submitted', 'FALSE', 'Present', '2025-09-17 10:35', 'https://submit.bcit.ca/comp2714/inlab/D003-L04-L02.pdf', '2025-09-19 11:35', 'https://submit.bcit.ca/comp2714/polished/D003-L04-L02.pdf', '7.0', '6.7', 'FALSE'),
('E001-L05-L01','E001', 'L05-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 14:45', 'https://submit.bcit.ca/comp2714/inlab/E001-L05-L01.pdf', '2025-09-11 16:45', 'https://submit.bcit.ca/comp2714/polished/E001-L05-L01.pdf', '8.5', '8.2', 'FALSE'),
('E001-L05-L02','E001', 'L05-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-17 14:35', 'https://submit.bcit.ca/comp2714/inlab/E001-L05-L02.pdf', '2025-09-19 15:35', 'https://submit.bcit.ca/comp2714/polished/E001-L05-L02.pdf', '7.0', '6.7', 'FALSE'),
('E002-L05-L01','E002', 'L05-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 14:45', 'https://submit.bcit.ca/comp2714/inlab/E002-L05-L01.pdf', '2025-09-11 16:45', 'https://submit.bcit.ca/comp2714/polished/E002-L05-L01.pdf', '8.5', '8.2', 'FALSE'),
('E002-L05-L02','E002', 'L05-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-17 14:40', 'https://submit.bcit.ca/comp2714/inlab/E002-L05-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('E003-L05-L01','E003', 'L05-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-10 14:45', 'https://submit.bcit.ca/comp2714/inlab/E003-L05-L01.pdf', '2025-09-11 16:45', 'https://submit.bcit.ca/comp2714/polished/E003-L05-L01.pdf', '8.5', '8.2', 'FALSE'),
('E003-L05-L02','E003', 'L05-L02',2, 'Submitted', 'FALSE', 'Present', '2025-09-17 14:35', 'https://submit.bcit.ca/comp2714/inlab/E003-L05-L02.pdf', '2025-09-19 15:35', 'https://submit.bcit.ca/comp2714/polished/E003-L05-L02.pdf', '7.0', '6.7', 'FALSE'),
('F001-L06-L01','F001', 'L06-L01',2, 'Submitted', 'TRUE', 'Present', '2025-09-11 19:45', 'https://submit.bcit.ca/comp2714/inlab/F001-L06-L02.pdf', '2025-09-12 21:45', 'https://submit.bcit.ca/comp2714/polished/F001-L06-L02.pdf', '7.0', '6.7', 'FALSE'),
('F001-L06-L02','F001', 'L06-L02',2, 'Submitted', 'TRUE', 'Present', '2025-09-18 19:35', 'https://submit.bcit.ca/comp2714/inlab/F001-L06-L02.pdf', '2025-09-20 20:35', 'https://submit.bcit.ca/comp2714/polished/F001-L06-L02.pdf', '7.0', '6.7', 'FALSE'),
('F002-L06-L01','F002', 'L06-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-11 19:45', 'https://submit.bcit.ca/comp2714/inlab/F002-L06-L01.pdf', '2025-09-12 21:45', 'https://submit.bcit.ca/comp2714/polished/F002-L06-L01.pdf', '8.5', '8.2', 'FALSE'),
('F002-L06-L02','F002', 'L06-L02',2, 'In Progress', 'TRUE', 'Present', '2025-09-18 19:40', 'https://submit.bcit.ca/comp2714/inlab/F002-L06-L02.pdf', NULL, NULL, NULL, NULL, 'FALSE'),
('F003-L06-L01','F003', 'L06-L01',1, 'Submitted', 'TRUE', 'Present', '2025-09-11 19:45', 'https://submit.bcit.ca/comp2714/inlab/F003-L06-L01.pdf', '2025-09-12 21:45', 'https://submit.bcit.ca/comp2714/polished/F003-L06-L01.pdf', '8.5', '8.2', 'FALSE'),
('F003-L06-L02','F003', 'L06-L02',2, 'Submitted', 'FALSE', 'Present', '2025-09-18 19:35', 'https://submit.bcit.ca/comp2714/inlab/F003-L06-L02.pdf', '2025-09-20 20:35', 'https://submit.bcit.ca/comp2714/polished/F003-L06-L02.pdf', '7.0', '6.7', 'FALSE');
-- Insert progress change logs
INSERT INTO progress_change_log
(chl_change_id, chl_progress_id, chl_changed_by, chl_changed_at, chl_field, chl_old_value, chl_new_value, chl_reason)
VALUES
('chg1', 'A001-L01-L01', 'u_instructor', '2025-09-09 12:10', 'instructor_assessment', '8.0', '8.5', 'Regraded after resubmission'),
('chg2', 'A003-L01-L02', 'u_ta1', '2025-09-16 20:45', 'status', 'In Progress', 'Submitted', 'Student submitted during lab; TA marked as submitted'),
('chg3', 'B003-L02-L01', 'u_system', '2025-09-23 23:59', 'late', 'False', 'True', 'Auto-flagged after set-specific due time');
+42
View File
@@ -0,0 +1,42 @@
-- Show all students with their section info Should work
SELECT s.student_first_name, s.student_last_name, ls.lab_course_code, c.course_title, t.term_name
FROM student s
JOIN student_section ss ON s.student_id = ss.ss_student_id
JOIN lab_section ls ON ss.ss_section_id = ls.lab_section_id
JOIN course c ON ls.lab_course_code = c.course_code
JOIN term t ON ls.lab_term_code = t.term_code;
-- Count students per lab section Should work
SELECT ls.lab_course_code, COUNT(ss.ss_student_id) AS total_students
FROM lab_section ls
LEFT JOIN student_section ss ON ls.lab_section_id = ss.ss_section_id
GROUP BY ls.lab_course_code;
-- View audit log Should work as intended
SELECT pcl.chl_change_id, ua.user_full_name AS actor, pcl.chl_reason, pcl.chl_changed_at
FROM progress_change_log pcl
JOIN user_account ua ON pcl.chl_changed_by = ua.user_id;
-- returns students names and sets
SELECT student_first_name, student_last_name, student_set_code
FROM student ORDER BY student_set_code ASC;
-- This should fail and not work
INSERT INTO course (course_code, course_title, credits)
VALUES ('COMP9999', 'Invalid Credits', 0);
-- Delete a lab section -> should also delete lab_event
-- and progress automatically cause of cascade
DELETE FROM lab_section WHERE lab_section_id = 'L01';
-- Check remaining events and progress
-- all should be empty due to cascade
SELECT * FROM lab_event WHERE lab_event.le_section_id = 'L01';
SELECT * FROM progress JOIN lab_event ON progress.pgr_event_id = lab_event.le_id
WHERE lab_event.le_section_id = 'L01';
SELECT * FROM progress_change_log JOIN progress ON progress_change_log.chl_progress_id = progress.pgr_id
JOIN lab_event ON progress.pgr_event_id = lab_event.le_id
WHERE lab_event.le_section_id = 'L01';
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+147
View File
@@ -0,0 +1,147 @@
SET search_path TO lab5;
-- TASK 1 Enforce Section Capacity
-- 1. Trigger function: ensure no course offering exceeds capacity
CREATE OR REPLACE FUNCTION check_offering_capacity()
RETURNS TRIGGER AS $$
DECLARE
v_capacity INTEGER;
v_current INTEGER;
BEGIN
SELECT off_capacity
INTO v_capacity
FROM course_offering
WHERE off_id = NEW.off_id;
SELECT COUNT(*)
INTO v_current
FROM enrollment
WHERE off_id = NEW.off_id
AND enr_status = 'Active';
IF v_current >= v_capacity THEN
RAISE EXCEPTION
'Cannot enroll student %. Section % is full (capacity=%).',
NEW.stu_num, NEW.off_id, v_capacity;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 2. Trigger
CREATE OR REPLACE TRIGGER trg_check_capacity
BEFORE INSERT ON enrollment
FOR EACH ROW
EXECUTE FUNCTION check_offering_capacity();
-- TASK 2 Enrollment Audit Logging
CREATE TABLE IF NOT EXISTS enrollment_audit (
audit_id SERIAL PRIMARY KEY,
enr_id_stu VARCHAR(20),
enr_id_off INTEGER,
action_type VARCHAR(10),
old_status VARCHAR(8),
new_status VARCHAR(8),
action_ts TIMESTAMP DEFAULT NOW(),
action_user TEXT DEFAULT current_user
);
CREATE OR REPLACE FUNCTION audit_enrollment_changes()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO enrollment_audit(
enr_id_stu,
enr_id_off,
action_type,
new_status
)
VALUES (
NEW.stu_num,
NEW.off_id,
'INSERT',
NEW.enr_status
);
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO enrollment_audit(
enr_id_stu,
enr_id_off,
action_type,
old_status,
new_status
)
VALUES (
NEW.stu_num,
NEW.off_id,
'UPDATE',
OLD.enr_status,
NEW.enr_status
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER trg_enrollment_audit
AFTER INSERT OR UPDATE ON enrollment
FOR EACH ROW
EXECUTE FUNCTION audit_enrollment_changes();
-- TASK 3 Validate Enrollment Status
ALTER TABLE enrollment DROP CONSTRAINT IF EXISTS enr_status_ck;
ALTER TABLE enrollment
ADD CONSTRAINT enr_status_ck
CHECK (enr_status IN ('Active', 'Dropped', 'Completed', 'Failed'));
CREATE OR REPLACE FUNCTION validate_enrollment_status()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.enr_status NOT IN ('Active','Dropped','Completed','Failed') THEN
RAISE EXCEPTION 'Invalid enrollment status: %', NEW.enr_status;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER trg_validate_status
BEFORE INSERT OR UPDATE OF enr_status ON enrollment
FOR EACH ROW
EXECUTE FUNCTION validate_enrollment_status();
-- TESTS
INSERT INTO enrollment VALUES ('S100', 1, 'Active');
INSERT INTO enrollment VALUES ('S101', 1, 'Active');
INSERT INTO enrollment VALUES ('S102', 1, 'Active');
INSERT INTO enrollment VALUES ('S200', 2, 'Active');
UPDATE enrollment SET enr_status = 'Dropped'
WHERE stu_num = 'S200' AND off_id = 2;
UPDATE enrollment
SET enr_status = 'Potato'
WHERE stu_num = 'S200' AND off_id = 2;
-- OUTPUT
-- SELECT * FROM enrollment;
-- SELECT * FROM enrollment_audit;
-- REFLECTION
-- Storing business rules directly inside the database ensures that
-- data remains correct and consistent no matter what application
-- or user interacts with it. Trigger-based validation centralizes
-- logic so errors are prevented at the source instead of being caught
-- later in the application.
+116
View File
@@ -0,0 +1,116 @@
-- 1. Create a schema for this lab (only runs once)
CREATE SCHEMA IF NOT EXISTS lab5;
-- 2. Tell PostgreSQL to use this schema by default
SET search_path TO lab5;
-- 3. Safety header: drop old tables if they exist
DROP TABLE IF EXISTS location CASCADE;
DROP TABLE IF EXISTS department CASCADE;
DROP TABLE IF EXISTS professor CASCADE;
DROP TABLE IF EXISTS student CASCADE;
DROP TABLE IF EXISTS term CASCADE;
DROP TABLE IF EXISTS course CASCADE;
DROP TABLE IF EXISTS course_offering CASCADE;
DROP TABLE IF EXISTS enrollment CASCADE;
-- 4. Create tables for lab5
CREATE TABLE location (
loc_building VARCHAR(10) NOT NULL,
loc_room VARCHAR(10) NOT NULL,
loc_code VARCHAR(20) GENERATED ALWAYS AS (
loc_building || ' ' || loc_room
) STORED PRIMARY KEY
);
CREATE TABLE department (
dep_code VARCHAR(10) PRIMARY KEY,
dep_name VARCHAR(50) NOT NULL,
dep_school VARCHAR(50) NOT NULL,
dep_phone VARCHAR(12) NOT NULL,
dep_email VARCHAR(50) NOT NULL,
dep_website VARCHAR(50) NOT NULL,
loc_code VARCHAR(10) REFERENCES location(loc_code)
);
CREATE TABLE professor (
prof_num VARCHAR(20) PRIMARY KEY,
prof_fname VARCHAR(20) NOT NULL,
prof_lname VARCHAR(20) NOT NULL,
prof_email VARCHAR(50) NOT NULL,
prof_phone VARCHAR(12) NOT NULL,
prof_hired DATE NOT NULL,
prof_office VARCHAR(20) REFERENCES location(loc_code) NOT NULL,
dep_code VARCHAR(50) REFERENCES department(dep_code) NOT NULL
);
CREATE TABLE term (
term_year INTEGER NOT NULL,
term_semester VARCHAR(10) NOT NULL,
term_name VARCHAR(30) GENERATED ALWAYS AS (
term_semester || ' ' || (term_year::text)
) STORED,
term_code VARCHAR(6) GENERATED ALWAYS AS (
(term_year::text) ||
CASE term_semester
WHEN 'Winter' THEN '10'
WHEN 'Spring' THEN '30'
WHEN 'Summer' THEN '40'
WHEN 'Fall' THEN '50'
END
) STORED,
CONSTRAINT term_pk PRIMARY KEY (term_code),
CONSTRAINT term_semester_ck CHECK (term_semester IN ('Winter','Spring','Summer','Fall'))
);
CREATE TABLE student (
stu_num VARCHAR(20) PRIMARY KEY,
stu_fname VARCHAR(20) NOT NULL,
stu_lname VARCHAR(20) NOT NULL,
stu_email VARCHAR(50) NOT NULL,
stu_phone VARCHAR(12) NOT NULL,
stu_program VARCHAR(5) NOT NULL,
stu_term VARCHAR(6) REFERENCES term(term_code) NOT NULL,
stu_dept VARCHAR(10) REFERENCES department(dep_code) NOT NULL
);
CREATE TABLE course (
crs_subject VARCHAR(10) REFERENCES department(dep_code) NOT NULL,
crs_num INTEGER NOT NULL,
crs_title VARCHAR(50) NOT NULL,
crd_credits NUMERIC(3,1) NOT NULL,
dep_code VARCHAR(10) REFERENCES department(dep_code) NOT NULL,
crs_desc TEXT NOT NULL,
crs_code VARCHAR(60) GENERATED ALWAYS AS (
dep_code || ' ' || crs_num::text
) STORED PRIMARY KEY
);
CREATE TABLE course_offering (
off_id SERIAL PRIMARY KEY,
off_crs_code VARCHAR(60) REFERENCES course(crs_code) NOT NULL,
off_term_code VARCHAR(6) REFERENCES term(term_code) NOT NULL,
off_section VARCHAR(50) NOT NULL,
off_type VARCHAR(10) NOT NULL,
off_prof VARCHAR(20) REFERENCES professor(prof_num) NOT NULL,
off_days VARCHAR(100) NOT NULL,
off_start_time TIME NOT NULL,
off_end_time TIME NOT NULL,
off_loc VARCHAR(20) REFERENCES location(loc_code) NOT NULL,
off_capacity INTEGER NOT NULL,
CONSTRAINT off_type_ck CHECK (off_type IN ('Lecture','Lab'))
);
CREATE TABLE enrollment (
stu_num VARCHAR(20) REFERENCES student(stu_num) NOT NULL,
off_id INTEGER REFERENCES course_offering(off_id) NOT NULL,
enr_status VARCHAR(8) NOT NULL,
enr_final_grade VARCHAR(2),
CONSTRAINT enr_pk PRIMARY KEY (stu_num, off_id),
CONSTRAINT enr_status_ck CHECK (enr_status IN ('Active', 'Dropped'))
);
+328
View File
@@ -0,0 +1,328 @@
TRUNCATE TABLE enrollment CASCADE;
TRUNCATE TABLE course_offering CASCADE;
TRUNCATE TABLE course CASCADE;
TRUNCATE TABLE term CASCADE;
TRUNCATE TABLE student CASCADE;
TRUNCATE TABLE professor CASCADE;
TRUNCATE TABLE department CASCADE;
TRUNCATE TABLE location CASCADE;
-- TASK 1
INSERT INTO location VALUES (
'SE12',
'240'
);
INSERT INTO location VALUES (
'SE12',
'260'
);
INSERT INTO location VALUES (
'SW01',
'1015'
);
INSERT INTO department VALUES (
'COMP',
'Computing',
'Business & Media',
'604-111-1111',
'comp@bcit.ca',
'https://bcit.ca/comp',
'SE12 240'
);
INSERT INTO department VALUES (
'MATH',
'Mathematics',
'Applied Sciences',
'604-222-2222',
'math@bcit.ca',
'https://bcit.ca/math',
'SE12 260'
);
INSERT INTO term VALUES (
2025,
'Fall'
);
INSERT INTO term VALUES (
2026,
'Winter'
);
INSERT INTO professor VALUES (
'A00123456',
'Ada',
'Nguyen',
'ada.nguyen@bcit.ca',
'604-300-1111',
'2019-08-15',
'SE12 240',
'COMP'
);
INSERT INTO professor VALUES (
'A00987654',
'Raj',
'Singh',
'raj.singh@bcit.ca',
'604-300-2222',
'2015-09-01',
'SE12 260',
'COMP'
);
INSERT INTO professor VALUES (
'A00777777',
'Maria',
'Lopez',
'maria.lopez@bcit.ca',
'604-300-3333',
'2012-01-10',
'SW01 1015',
'MATH'
);
INSERT INTO student VALUES (
'A10000001',
'Tom',
'Anderson',
'tom.anderson@my.bcit.ca',
'604-777-1111',
'CST',
'202550',
'COMP'
);
INSERT INTO student VALUES (
'A10000002',
'Sara',
'Young',
'sara.young@my.bcit.ca',
'604-777-2222',
'CST',
'202550',
'COMP'
);
INSERT INTO student VALUES (
'A10000003',
'Reena',
'Patel',
'reena.patel@my.bcit.ca',
'604-777-3333',
'ACIT',
'202550',
'COMP'
);
INSERT INTO student VALUES (
'A10000004',
'Min',
'Chen',
'min.chen@my.bcit.ca',
'604-777-4444',
'MATH',
'202610',
'MATH'
);
INSERT INTO course VALUES (
'COMP',
2714,
'Relational Database Systems',
4.0,
'COMP',
'Core RDBMS course'
);
INSERT INTO course VALUES (
'COMP',
1537,
'Web Development 1',
3.0,
'COMP',
'Intro to web dev'
);
INSERT INTO course VALUES (
'MATH',
3042,
'Discrete Mathematics',
4.0,
'MATH',
'Proofs & structures'
);
INSERT INTO course_offering (
off_crs_code, off_term_code, off_section, off_type,
off_prof, off_days, off_start_time, off_end_time,
off_loc, off_capacity
) VALUES (
'COMP 2714',
'202550',
'LEC',
'Lecture',
'A00123456',
'Mon,Wed',
'09:30',
'11:20',
'SE12 240',
'60'
);
INSERT INTO course_offering (
off_crs_code, off_term_code, off_section, off_type,
off_prof, off_days, off_start_time, off_end_time,
off_loc, off_capacity
) VALUES (
'COMP 2714',
'202550',
'L1',
'Lab',
'A00987654',
'Fri',
'12:30',
'14:20',
'SE12 260',
'24'
);
INSERT INTO enrollment VALUES (
'A10000001',
1,
'Active',
NULL
);
INSERT INTO enrollment VALUES (
'A10000002',
1,
'Active',
NULL
);
INSERT INTO enrollment VALUES (
'A10000003',
1,
'Active',
NULL
);
INSERT INTO enrollment VALUES (
'A10000001',
2,
'Active',
NULL
);
INSERT INTO enrollment VALUES (
'A10000002',
2,
'Active',
NULL
);
-- TASK 2
INSERT INTO student VALUES (
'A01385066',
'Braeden',
'Sowinski',
'bsowinski@my.bcit.ca',
'778-208-8109',
'CST',
'202550',
'COMP'
);
INSERT INTO professor VALUES (
'A11223344',
'Jason',
'Wilder',
'jason.wilder@bcit.ca',
'777-666-5555',
'2020-01-01',
'SE12 260',
'COMP'
);
INSERT INTO course_offering (
off_crs_code, off_term_code, off_section, off_type,
off_prof, off_days, off_start_time, off_end_time,
off_loc, off_capacity
) VALUES (
'COMP 2714',
'202550',
'L2',
'Lab',
'A11223344',
'Tue',
'13:30',
'15:20',
'SE12 260',
'20'
);
INSERT INTO enrollment VALUES (
'A01385066',
3,
'Active',
NULL
);
INSERT INTO enrollment VALUES (
'A10000002',
3,
'Active',
NULL
);
-- TASK 3
-- Insert location for update
INSERT INTO location (loc_building, loc_room)
VALUES ('SW01', '1015')
ON CONFLICT DO NOTHING; -- if already exists, skip
UPDATE course_offering
SET off_days = 'Thu',
off_start_time = '15:30',
off_end_time = '17:20',
off_loc = 'SW01 1015'
WHERE off_section = 'L1';
-- Update student A10000002 for course_offering 3
UPDATE enrollment
SET enr_status = 'Dropped'
WHERE off_id = 3 -- L2 Lab Comp 2714
AND stu_num = 'A10000002'; -- Sara Young
UPDATE enrollment
SET enr_status = 'Active'
WHERE off_id = 3 -- L2 Lab Comp 2714
AND stu_num = 'A10000002'; -- Sara Young
-- Update grades
UPDATE enrollment
SET enr_final_grade = 'A'
WHERE off_id = 1 -- Lecture Comp 2714
AND stu_num = 'A10000001'; -- Tom Anderson
UPDATE enrollment
SET enr_final_grade = 'A'
WHERE off_id = 1 -- Lecture Comp 2714
AND stu_num = 'A10000003'; -- Reena Patel
-- TASK 4
-- DELETE FROM department
-- WHERE dep_code = 'COMP';
-- DELETE FROM course_offering
-- WHERE off_section = 'L2';
-- DELETE FROM location
-- WHERE loc_code = 'SE12 260';
+14
View File
@@ -0,0 +1,14 @@
Almost all the tables and or constraints caused errors, so I had to spend a lot of time, updating the lab5 schema sql file to accomodate the data given for lab 6 as well as update some constraints to ensure all the insertions worked as intended.
Here is an example of some of the error messages given while attemping to delete data in task 4 that is referenced by entities in other tables.
psql:lab6_dml.sql:322: ERROR: update or delete on table "department" violates foreign key constraint "professor_dep_code_fkey" on table "professor"
DETAIL: Key (dep_code)=(COMP) is still referenced from table "professor".
psql:lab6_dml.sql:325: ERROR: column "off_sectoin" does not exist
LINE 2: WHERE off_sectoin = 'L2';
^
HINT: Perhaps you meant to reference the column "course_offering.off_section".
psql:lab6_dml.sql:328: ERROR: update or delete on table "location" violates foreign key constraint "department_loc_code_fkey" on table "department"
DETAIL: Key (loc_code)=(SE12 260) is still referenced from table "department".
Finally, I think the schema is much better not only to fit the data given but being correct and making sense logically on what data should be there, and what their constraints should be to better reflect the real world.
+108
View File
@@ -0,0 +1,108 @@
-- Braeden Sowinski
-- Set 2D
SET search_path TO lab5;
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'lab5';
-- Quick peek per table
SELECT * FROM student LIMIT 5;
-- ### PART 1 - Basic SELECT ###
-- Q1.1 List all students first and last names.
SELECT stu_fname, stu_lname FROM student;
-- Q1.2 List all courses (code + title).
SELECT crs_code, crs_title FROM course;
-- Q1.3 Show all lab offerings for for COMP 2714 in Fall 2025 (days + start time).
SELECT off_days, off_start_time
FROM course_offering
WHERE off_crs_code = 'COMP 2714'
AND off_term_code = '202550' -- Fall 2025
AND off_type = 'Lab';
-- Q1.4 Display all rows from your enrollment table.
SELECT * FROM enrollment;
-- ### PART 2 - INNER JOIN Practice ###
-- Q2.1 List all students and the sections they are enrolled in. Include student name, section id, and term.
SELECT student.stu_fname, course_offering.off_section, course_offering.off_term_code
FROM student
INNER JOIN enrollment
ON student.stu_num = enrollment.stu_num
INNER JOIN course_offering
ON enrollment.off_id = course_offering.off_id;
-- Q2.2 Show all course offerings with their course titles.
SELECT course_offering.off_id, course_offering.off_section, course_offering.off_type, course.crs_title
FROM course_offering
INNER JOIN course
ON course_offering.off_crs_code = course.crs_code;
-- Q2.3 Display enrollments with the course title and the meeting days.
SELECT enrollment.stu_num, enrollment.enr_status, course.crs_title, course_offering.off_days
FROM enrollment
INNER JOIN course_offering
ON enrollment.off_id = course_offering.off_id
INNER JOIN course
ON course_offering.off_crs_code = course.crs_code;
-- ### PART 3 - FILTERING & SORTING ###
-- Q3.1 Students enrolled in Fall 2025 only (term code 202550).
SELECT student.stu_fname, enrollment.enr_status, course_offering.off_term_code
FROM student
INNER JOIN enrollment
ON student.stu_num = enrollment.stu_num
INNER JOIN course_offering
ON enrollment.off_id = course_offering.off_id
WHERE off_term_code = '202550';
-- Q3.2 Courses whose title contains the word Database (case-insensitive).
SELECT *
FROM course
WHERE LOWER(crs_title) LIKE LOWER('%database%');
-- No contains? LOWER() ensures that it doesnt matter if upper or lower case
-- Q3.4 Students whose last name begins with C.
SELECT *
FROM student
WHERE stu_lname LIKE 'C%';
-- ### PART 4 - OUTER JOINs & NULLs
-- Q4.1 All students with their enrollment info, including students with no enrollments (use LEFT JOIN).
SELECT student.stu_fname, enrollment.*
FROM student
LEFT JOIN enrollment
ON student.stu_num = enrollment.stu_num;
-- Q4.2 Courses not offered in the current term.
SELECT course.crs_title, course.crs_subject, course.crs_credits
FROM course
LEFT JOIN course_offering
ON course_offering.off_crs_code = course.crs_code
WHERE course_offering.off_term_code IS NULL;
-- ### REFLECTION ###
--
-- Yes, all of the row counts make sense, there may
-- be some duplicatoin. For example, when we are
-- getting all student ids, and their enrollments,
-- there may be duplicate enrollment data.
-- i.e. two students are enrolled in the same
-- course_offering. However, we since we are showing
-- the student_id we are able to tell them apart.
-- I dont think distinct would change the result
-- in the example above, there is student x enrolled
-- in class A, and student y enrolled in class A too.
-- Those are already distinct.
-- Yes I think I am selecting the minimul necessary
-- columns, only showing relavent related information.
+9
View File
@@ -0,0 +1,9 @@
Yes, all of the row counts make sense, there may be some duplicatoin.
For example, when we are getting all student ids, and their enrollments, there may be duplicate enrollment data.
i.e. two students are enrolled in the same course_offering. However, we since we are showing the student_id we are able to tell them apart.
I dont think distinct would change the result in the example above, there is student x enrolled in class A, and student y enrolled in class A too. Those are already distinct.
I think for the most part I am selecting the minimul necessary columns, only showing relavent related information.
Unless asked to show all data of a table.