Exercises & Answer Key
The exercise statement is open to read freely. The answer key and detailed explanations are reserved for students currently enrolled in the IT004 practicum class and require an access code from the instructor.
EXERCISE · PARTS I-III
Academic Affairs Management - Questions 1 ➜ 50
Week 4 first practices a few extended functions (CAST, GETDATE, LEFT, RIGHT), CASE WHEN, and Alias/Subquery, then applies them across 50 main questions on the Academic Affairs Management schema: Part I - Data Definition Language (questions 1-11), Part II - Data Manipulation Language (questions 1-4), Part III - Data Query Language (questions 1-35). Try it yourself before checking the answer key. Full question details are in the PDF below.
<MSSV>_<HoVaTen>_BTTH4.sql (MSSV is your student ID, HoVaTen is your full name).Part I - Data Definition Language (questions 1-11)
- Create the relations and declare all primary key and foreign key constraints. Add the 3 attributes
GHICHU,DIEMTB,XEPLOAIto the relationHOCVIEN. - The attribute
GIOITINHmay only hold "Nam" or "Nu". - An exam score must be between 0 and 10 and needs to be stored with 2 decimal places (e.g. 6.22).
- The exam result is "Dat" if the score is from 5 to 10, and "Khong dat" if the score is below 5.
- A student may take an exam for a given subject at most 3 times.
- The semester value can only be from 1 to 3.
- A teacher's degree can only be one of "CN", "KS", "Ths", "TS", "PTS".
- A student must be at least 18 years old.
- For a course being taught, the start date (
TUNGAY) must be earlier than the end date (DENNGAY). - A teacher must be at least 22 years old when hired.
- For every subject, the theory credit count and the practice credit count may not differ by more than 3.
Part II - Data Manipulation Language (questions 1-4)
- Increase the salary coefficient by 0.2 for teachers who are department heads.
- Update the average score (
DIEMTB) across all subjects for each student (every subject has weight 1, and if a student has taken an exam multiple times, only the score from the most recent attempt counts). - Update the
GHICHUcolumn to "Cam thi" for the case where: a student scores below 5 on their 3rd attempt at any subject. - Update the
XEPLOAIcolumn in the relationHOCVIENas follows: "XS" ifDIEMTB≥ 9 · "G" if 8 ≤DIEMTB< 9 · "K" if 6.5 ≤DIEMTB< 8 · "TB" if 5 ≤DIEMTB< 6.5 · "Y" ifDIEMTB< 5.
Part III - Data Query Language (questions 1-35)
- Print the list (student ID, full name, date of birth, class code) of each class's class monitor.
- Print the exam scoresheet (student ID, full name, attempt number, score) for the CTRR subject in class "K12", sorted by student first and last name.
- Print the list of students (student ID, full name) and the subjects they passed on their first attempt.
- Print the list of students (student ID, full name) in class "K11" who failed the CTRR subject (on attempt 1).
- * List of students (student ID, full name) in class "K" who failed the CTRR subject (across all attempts).
- Find the names of the subjects taught by the teacher named "Tran Tam Thanh" in semester 1 of 2006.
- Find the subjects (subject code, subject name) taught by the homeroom teacher of class "K11" in semester 1 of 2006.
- Find the full name of the class monitor of the class(es) where the teacher named "Nguyen To Lan" teaches "Co So Du Lieu".
- Print the list of subjects (subject code, subject name) that must be completed as a prerequisite before "Co So Du Lieu".
- Which subjects (subject code, subject name) require "Cau Truc Roi Rac" as a mandatory prerequisite?
- Find the full name of the teacher who taught CTRR to both class "K11" and class "K12" in the same semester 1 of 2006.
- Find the students (student ID, full name) who failed the CSDL subject on their 1st attempt but have not yet retaken it.
- Find the teachers (teacher ID, full name) not assigned to teach any subject.
- Find the teachers (teacher ID, full name) not assigned to teach any subject belonging to the department they head.
- Find the full names of students in class "K11" who either failed any subject on more than 3 attempts and are still "Khong dat", or scored exactly 5 on their 2nd attempt at CTRR.
- Find the full name of the teacher who taught CTRR to at least 2 classes in the same semester of the same academic year.
- List of students and their CSDL exam scores (using only the score from the most recent attempt).
- List of students and their "Co So Du Lieu" exam scores (using the highest score across all attempts).
- Which department (department code, department name) was established earliest?
- How many teachers hold the academic rank "GS" or "PGS"?
- Report how many teachers hold the degree "CN", "KS", "Ths", "TS", "PTS" within each department.
- For each subject, report the number of students by result (passed and failed).
- Find the teachers (teacher ID, full name) who are the homeroom teacher of a class and also teach at least one subject to that same class.
- Find the full name of the class monitor of the class with the largest enrollment.
- * Find the full names of class monitors (LOPTRG) who failed more than 3 subjects (each subject failed on every attempt).
- Find the student (student ID, full name) with the most subjects scored 9 or 10.
- Within each class, find the student (student ID, full name) with the most subjects scored 9 or 10.
- For each semester of each academic year, report how many subjects and how many classes each teacher was assigned to teach.
- For each semester of each academic year, find the teacher (teacher ID, full name) who taught the most.
- Find the subject (subject code, subject name) with the highest number of students who failed (on the 1st attempt).
- Find students (student ID, full name) who passed every subject they took (considering only the 1st attempt).
- * Find students (student ID, full name) who passed every subject they took (considering only the most recent attempt).
- * Find students (student ID, full name) who passed all subjects taken (considering only the 1st attempt).
- * Find students (student ID, full name) who passed all subjects taken (considering only the most recent attempt).
- ** Find the student (student ID, full name) with the highest score in each subject (using the most recent attempt's score).
ANSWER
View answer
Protected content
Enter the access code to view the Week 4 answer.
Access code provided by the instructor.
Full answer key for Practice 04 - Academic Affairs Management (Part I 11 questions · Part II 4 questions · Part III 35 questions). Retype each answer into SSMS to remember it better - the Copy button here is purely for convenience.
QUESTION 1 (PART I) · CREATE TABLE
Exercise: Create the relations and declare all primary key and foreign key constraints. Add the 3 attributes GHICHU, DIEMTB, XEPLOAI to the relation HOCVIEN.
CREATE DATABASE QUANLIGIAOVU_2026;
GO
USE QUANLIGIAOVU_2026;
GO
CREATE TABLE KHOA
(
MAKHOA CHAR(4) PRIMARY KEY,
TENKHOA VARCHAR(40),
NGTLAP SMALLDATETIME,
TRGKHOA CHAR(4)
);
CREATE TABLE GIAOVIEN
(
MAGV CHAR(4) PRIMARY KEY,
HOTEN VARCHAR(40),
HOCVI VARCHAR(10),
HOCHAM VARCHAR(10),
GIOITINH VARCHAR(3),
NGSINH SMALLDATETIME,
NGVL SMALLDATETIME,
HESO NUMERIC(4,2),
MUCLUONG MONEY,
MAKHOA CHAR(4),
FOREIGN KEY (MAKHOA) REFERENCES KHOA(MAKHOA)
);
ALTER TABLE KHOA ADD CONSTRAINT FK_KHOA_TRGKHOA
FOREIGN KEY (TRGKHOA) REFERENCES GIAOVIEN(MAGV);
CREATE TABLE LOP
(
MALOP CHAR(3) PRIMARY KEY,
TENLOP VARCHAR(40),
TRGLOP CHAR(5),
SISO TINYINT,
MAGVCN CHAR(4),
FOREIGN KEY (MAGVCN) REFERENCES GIAOVIEN(MAGV)
);
CREATE TABLE HOCVIEN
(
MAHV CHAR(5) PRIMARY KEY,
HO VARCHAR(40),
TEN VARCHAR(10),
NGSINH SMALLDATETIME,
GIOITINH VARCHAR(3),
NOISINH VARCHAR(40),
MALOP CHAR(3),
GHICHU VARCHAR(20),
DIEMTB NUMERIC(4,2),
XEPLOAI VARCHAR(5),
FOREIGN KEY (MALOP) REFERENCES LOP(MALOP)
);
ALTER TABLE LOP ADD CONSTRAINT FK_LOP_TRGLOP
FOREIGN KEY (TRGLOP) REFERENCES HOCVIEN(MAHV);
CREATE TABLE MONHOC
(
MAMH CHAR(10) PRIMARY KEY,
TENMH VARCHAR(40),
TCLT TINYINT,
TCTH TINYINT,
MAKHOA CHAR(4),
FOREIGN KEY (MAKHOA) REFERENCES KHOA(MAKHOA)
);
CREATE TABLE DIEUKIEN
(
MAMH CHAR(10),
MAMH_TRUOC CHAR(10),
PRIMARY KEY (MAMH, MAMH_TRUOC),
FOREIGN KEY (MAMH) REFERENCES MONHOC(MAMH),
FOREIGN KEY (MAMH_TRUOC) REFERENCES MONHOC(MAMH)
);
CREATE TABLE GIANGDAY
(
MALOP CHAR(3),
MAMH CHAR(10),
MAGV CHAR(4),
HOCKY TINYINT,
NAM SMALLINT,
TUNGAY SMALLDATETIME,
DENNGAY SMALLDATETIME,
PRIMARY KEY (MALOP, MAMH),
FOREIGN KEY (MALOP) REFERENCES LOP(MALOP),
FOREIGN KEY (MAMH) REFERENCES MONHOC(MAMH),
FOREIGN KEY (MAGV) REFERENCES GIAOVIEN(MAGV)
);
CREATE TABLE KETQUATHI
(
MAHV CHAR(5),
MAMH CHAR(10),
LANTHI TINYINT,
NGTHI SMALLDATETIME,
DIEM NUMERIC(4,2),
KQUA VARCHAR(10),
PRIMARY KEY (MAHV, MAMH, LANTHI),
FOREIGN KEY (MAHV) REFERENCES HOCVIEN(MAHV),
FOREIGN KEY (MAMH) REFERENCES MONHOC(MAMH)
);All 8 relations are created with CREATE TABLE; foreign keys are declared right in the column definitions once the referenced table already exists. The 3 new columns GHICHU, DIEMTB, XEPLOAI are added straight into HOCVIEN at creation time.
Table creation order must follow the foreign key dependency chain - GIAOVIEN waits for KHOA, HOCVIEN waits for LOP. KHOA.TRGKHOA ↔ GIAOVIEN.MAKHOA and LOP.TRGLOP ↔ HOCVIEN.MALOP reference each other in a cycle, so both foreign keys can't be declared at once inside CREATE TABLE - One direction has to be created first, then ALTER TABLE ADD CONSTRAINT adds the other direction once the other table exists.
QUESTION 2 (PART I) · CHECK ... IN
Exercise: The attribute GIOITINH may only hold "Nam" or "Nu".
ALTER TABLE HOCVIEN ADD CONSTRAINT CK_HOCVIEN_GIOITINH
CHECK (GIOITINH IN (N'Nam', N'Nu'));
ALTER TABLE GIAOVIEN ADD CONSTRAINT CK_GIAOVIEN_GIOITINH
CHECK (GIOITINH IN (N'Nam', N'Nu'));CHECK ... IN restricts a column to exactly the 2 listed values, regardless of which table the column belongs to.
GIOITINH appears in both HOCVIEN and GIAOVIEN in this schema, and the question doesn't specify which relation - the constraint is applied to both so the data stays consistent everywhere the column appears.
QUESTION 3 (PART I) · CHECK
Exercise: An exam score must be between 0 and 10 and needs to be stored with 2 decimal places (e.g. 6.22).
ALTER TABLE KETQUATHI ADD CONSTRAINT CK_KETQUATHI_DIEM
CHECK (DIEM BETWEEN 0 AND 10);Alternative approach (string format check)
ALTER TABLE KETQUATHI ADD CONSTRAINT CK_KETQUATHI_DIEM
CHECK (
DIEM BETWEEN 0 AND 10
AND RIGHT(CAST(DIEM AS VARCHAR), 3) LIKE '.__'
);RIGHT(CAST(DIEM AS VARCHAR), 3) LIKE '.__' takes the last 3 characters of the number's string form and requires a decimal point followed by exactly 2 characters (e.g. "6.22" ➜ ".22" matches). This is useful when a column's type doesn't already enforce a fixed scale on its own - here NUMERIC(4,2) already guarantees exactly 2 decimal places, so it isn't strictly necessary, but it's a good technique to know for enforcing a number's display format rather than just its value.
BETWEEN 0 AND 10 limits the value to a closed range - both 0 and 10 are valid.
The "2 decimal places" part is already satisfied by the NUMERIC(4,2) type declared for DIEM in Question 1 (4 digits total, 2 after the decimal point) - This question only still needs the 0-10 range constraint.
QUESTION 4 (PART I) · CHECK
Exercise: The exam result is "Dat" if the score is from 5 to 10, and "Khong dat" if the score is below 5.
ALTER TABLE KETQUATHI ADD CONSTRAINT CK_KETQUATHI_KQUA
CHECK (
(DIEM >= 5 AND KQUA = N'Dat')
OR (DIEM < 5 AND KQUA = N'Khong dat')
);CHECK constrains the relationship between 2 columns in the same row - here DIEM and KQUA must always agree.
The 2 conditions can't be split into 2 separate CHECK constraints because they depend on each other (whether KQUA is correct depends on DIEM) - Combining both cases in 1 CHECK with OR keeps the data consistent no matter how high or low DIEM is.
QUESTION 5 (PART I) · CHECK
Exercise: A student may take an exam for a given subject at most 3 times.
ALTER TABLE KETQUATHI ADD CONSTRAINT CK_KETQUATHI_LANTHI
CHECK (LANTHI BETWEEN 1 AND 3);Restricts LANTHI to only 1, 2, or 3.
The primary key (MAHV, MAMH, LANTHI) already guarantees no duplicate attempt number for a given (student, subject) pair - combined with the CHECK capping LANTHI at 3, a student can have at most 3 exam result rows for the same subject.
QUESTION 6 (PART I) · CHECK
Exercise: The semester value can only be from 1 to 3.
ALTER TABLE GIANGDAY ADD CONSTRAINT CK_GIANGDAY_HOCKY
CHECK (HOCKY BETWEEN 1 AND 3);Alternative approach (IN)
ALTER TABLE GIANGDAY ADD CONSTRAINT CK_GIANGDAY_HOCKY
CHECK (HOCKY IN (1, 2, 3));IN (1, 2, 3) gives the same result as BETWEEN 1 AND 3 since these are consecutive integers - BETWEEN is more compact for a continuous range, while IN fits better if the valid semester list ever stops being continuous (e.g. only 1 and 3, dropping semester 2).
Same technique as Question 5, applied to the HOCKY column of GIANGDAY.
An academic year has only 3 semesters at this training center - BETWEEN 1 AND 3 blocks any invalid semester value right at data entry.
QUESTION 7 (PART I) · CHECK ... IN
Exercise: A teacher's degree can only be one of "CN", "KS", "Ths", "TS", "PTS".
ALTER TABLE GIAOVIEN ADD CONSTRAINT CK_GIAOVIEN_HOCVI
CHECK (HOCVI IN (N'CN', N'KS', N'Ths', N'TS', N'PTS'));CHECK ... IN with a closed list of 5 valid degrees.
The question lists exactly 5 degrees, with no trailing "…" - this is a closed list, so IN (...) is the right fit.
QUESTION 8 (PART I) · CHECK ... GETDATE
Exercise: A student must be at least 18 years old.
ALTER TABLE HOCVIEN ADD CONSTRAINT CK_HOCVIEN_TUOI
CHECK (NGSINH <= DATEADD(YEAR, -18, GETDATE()));Alternative approach (YEAR)
ALTER TABLE HOCVIEN ADD CONSTRAINT CK_HOCVIEN_TUOI
CHECK (YEAR(GETDATE()) - YEAR(NGSINH) >= 18);This is short and commonly seen, but only compares calendar years and ignores month/day - a student born on December 31st would already count as 18 at the very start of that year, even though they're actually still almost a year short of turning 18. The DATEADD version above is exact to the day, so it stays the recommended choice.
CHECK (GETDATE() - NGSINH >= 18) (subtracting the 2 date columns directly and comparing to 18) looks like the right direction but is completely wrong. Subtracting 2 date values in SQL Server returns a difference measured in days, not years - comparing that to the plain integer 18 only checks "born at least 18 days ago", not 18 years.DATEADD(YEAR, -18, GETDATE()) computes the date exactly 18 years before today; a valid student must be born on or before that date.
SQL Server allows a CHECK to reference GETDATE(), but the constraint is only evaluated at INSERT/UPDATE time - a student who was valid when inserted stays in the table as-is even as time passes; CHECK does not automatically re-scan existing rows.
QUESTION 9 (PART I) · CHECK
Exercise: For a course being taught, the start date (TUNGAY) must be earlier than the end date (DENNGAY).
ALTER TABLE GIANGDAY ADD CONSTRAINT CK_GIANGDAY_NGAY
CHECK (TUNGAY < DENNGAY);A direct comparison between 2 date columns in the same row.
A course can't end before it starts - this constraint keeps the teaching period logically valid.
QUESTION 10 (PART I) · CHECK
Exercise: A teacher must be at least 22 years old when hired.
ALTER TABLE GIAOVIEN ADD CONSTRAINT CK_GIAOVIEN_TUOI_VAOLAM
CHECK (NGVL >= DATEADD(YEAR, 22, NGSINH));Alternative approach (YEAR)
ALTER TABLE GIAOVIEN ADD CONSTRAINT CK_GIAOVIEN_TUOI_VAOLAM
CHECK (YEAR(NGVL) - YEAR(NGSINH) >= 22);Same drawback as the YEAR approach in Question 8 - it doesn't account for month/day precisely, so it can overcount a few months of age for a teacher born late in the year. Note: Writing CHECK ((NGVL - NGSINH) >= 22) (subtracting the 2 date columns directly) is wrong in the exact same way as the mistake flagged in Question 8 - the subtraction result is a difference in days, so comparing it to 22 only checks "at least 22 days apart", not 22 years.
DATEADD(YEAR, 22, NGSINH) computes the date the teacher turns 22; NGVL must be on or after that date.
Unlike Question 8, this constraint compares 2 columns within the same row (NGSINH and NGVL) instead of against GETDATE() - so it stays correct and stable forever, without going stale the way a current-date CHECK would.
QUESTION 11 (PART I) · CHECK ... ABS
Exercise: For every subject, the theory credit count and the practice credit count may not differ by more than 3.
ALTER TABLE MONHOC ADD CONSTRAINT CK_MONHOC_TC
CHECK (ABS(TCLT - TCTH) <= 3);ABS(...) takes the absolute value, so the difference is computed correctly whether TCLT is larger or smaller than TCTH.
The question only says the difference "may not exceed 3" without caring which side is larger - writing TCLT - TCTH <= 3 without ABS would let a case where TCTH is much larger than TCLT slip through, which contradicts the intended meaning.
QUESTION 1 (PART II) · UPDATE ... SUBQUERY
Exercise: Increase the salary coefficient by 0.2 for teachers who are department heads.
UPDATE GIAOVIEN
SET HESO = HESO + 0.2
WHERE MAGV IN (
SELECT TRGKHOA FROM KHOA WHERE TRGKHOA IS NOT NULL
);The subquery gets every MAGV that is currently the TRGKHOA of any department; IN filters the UPDATE to exactly those teachers.
TRGKHOA can be NULL (the KTMT department has no department head yet). With IN, filtering out NULL isn't strictly required since NULL never matches but also never causes a wrong result - however it's a good habit to keep the IS NOT NULL filter, because if this were switched to NOT IN in another exercise, a NULL in the list would make the whole statement return nothing - a very easy mistake to make.
QUESTION 2 (PART II) · UPDATE ... CORRELATED SUBQUERY
Exercise: Update the average score (DIEMTB) across all subjects for each student (every subject has weight 1, and if a student has taken an exam multiple times, only the score from the most recent attempt counts).
UPDATE HOCVIEN
SET DIEMTB = (
SELECT AVG(kq.DIEM)
FROM KETQUATHI kq
WHERE kq.MAHV = HOCVIEN.MAHV
AND kq.LANTHI = (
SELECT MAX(kq2.LANTHI)
FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = kq.MAMH
)
);Alternative approach (UPDATE ... FROM ... JOIN)
UPDATE HOCVIEN
SET DIEMTB = t.Diem
FROM HOCVIEN
JOIN (
SELECT kq.MAHV, AVG(kq.DIEM) AS Diem
FROM KETQUATHI kq
WHERE kq.LANTHI = (
SELECT MAX(kq2.LANTHI) FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = kq.MAMH
)
GROUP BY kq.MAHV
) t ON HOCVIEN.MAHV = t.MAHV;The UPDATE ... FROM ... JOIN syntax (a SQL Server feature) computes the average once with GROUP BY in the derived table t, then JOINs it straight into HOCVIEN - it gives the same result as the scalar subquery version above, but runs the averaging calculation once for all students instead of re-running the subquery per row, so it's usually faster on a large table. Safety tip: run the SELECT inside t on its own first to sanity check the numbers before running the UPDATE.
The inner subquery uses MAX(LANTHI) to keep only the most recent attempt for each (MAHV, MAMH) pair; the outer subquery runs AVG(DIEM) over exactly that filtered set for each student.
You can't run AVG(DIEM) directly over the whole KETQUATHI table, since a student can have multiple rows for the same subject (retakes) - without filtering to the most recent attempt, the average would be skewed by earlier failed attempts too.
QUESTION 3 (PART II) · UPDATE ... SUBQUERY
Exercise: Update the GHICHU column to "Cam thi" for the case where: a student scores below 5 on their 3rd attempt at any subject.
UPDATE HOCVIEN
SET GHICHU = N'Cam thi'
WHERE MAHV IN (
SELECT MAHV
FROM KETQUATHI
WHERE LANTHI = 3 AND DIEM < 5
);The subquery gets the MAHV of every 3rd-attempt result scoring below 5; IN then flags exactly those students.
Just 1 qualifying subject is enough to trigger "Cam thi" ("any subject") - no GROUP BY or counting is needed, the student just has to appear at least once in the filtered result set.
QUESTION 4 (PART II) · UPDATE ... CASE WHEN
Exercise: Update the XEPLOAI column in the relation HOCVIEN as follows: "XS" if DIEMTB ≥ 9 · "G" if 8 ≤ DIEMTB < 9 · "K" if 6.5 ≤ DIEMTB < 8 · "TB" if 5 ≤ DIEMTB < 6.5 · "Y" if DIEMTB < 5.
UPDATE HOCVIEN
SET XEPLOAI = CASE
WHEN DIEMTB >= 9 THEN N'XS'
WHEN DIEMTB >= 8 THEN N'G'
WHEN DIEMTB >= 6.5 THEN N'K'
WHEN DIEMTB >= 5 THEN N'TB'
ELSE N'Y'
END;CASE WHEN checks conditions in order from the highest threshold down - SQL Server stops at the first true condition, so each branch doesn't need to spell out both an upper and a lower bound.
For example the "G" branch only needs DIEMTB >= 8, without an extra AND DIEMTB < 9 - because if DIEMTB were really ≥ 9, the "XS" branch above it would already have matched first and CASE would have stopped there. Adding an upper-bound condition would be redundant.
QUESTION 1 (PART III) · JOIN
Exercise: Print the list (student ID, full name, date of birth, class code) of each class's class monitor.
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, hv.NGSINH, hv.MALOP
FROM HOCVIEN hv
JOIN LOP l ON hv.MAHV = l.TRGLOP;Alternative approach (subquery IN)
SELECT MAHV, HO + N' ' + TEN AS HOTEN, NGSINH, MALOP
FROM HOCVIEN
WHERE MAHV IN (SELECT TRGLOP FROM LOP);Since the question only needs columns from HOCVIEN (nothing from LOP), JOIN can be replaced with an IN subquery that filters HOCVIEN directly without joining any table - more compact, but only works when no extra column is needed from the other table.
JOIN HOCVIEN with LOP via TRGLOP to get exactly the students who are currently a class monitor; HO + ' ' + TEN concatenates the full name since HOCVIEN stores it as 2 separate columns.
JOIN is used (not a subquery) because we need 4 full columns of student data - the question asks for both full name and date of birth, so we must join directly into HOCVIEN.
QUESTION 2 (PART III) · JOIN ... ORDER BY
Exercise: Print the exam scoresheet (student ID, full name, attempt number, score) for the CTRR subject in class "K12", sorted by student first and last name.
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, kq.LANTHI, kq.DIEM
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE hv.MALOP = N'K12' AND kq.MAMH = N'CTRR'
ORDER BY hv.TEN, hv.HO;JOIN HOCVIEN with KETQUATHI via MAHV, filtering for the right class and subject; ORDER BY TEN before HO because sorting "by first name, then last name" means the first (given) name takes priority.
No need to JOIN in LOP as well, since MALOP already lives directly on HOCVIEN - there's no need to go through LOP just to filter for class "K12".
QUESTION 3 (PART III) · JOIN
Exercise: Print the list of students (student ID, full name) and the subjects they passed on their first attempt.
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, kq.MAMH
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.LANTHI = 1 AND kq.KQUA = N'Dat';Alternative approach (filtering by DIEM)
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, kq.MAMH
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.LANTHI = 1 AND kq.DIEM >= 5;Thanks to the CHECK constraint from Question 4 (Part I) guaranteeing DIEM and KQUA always agree (DIEM >= 5 if and only if KQUA = 'Dat'), filtering by DIEM >= 5 or by KQUA = 'Dat' always gives the same result - either column works, as long as that constraint stays in place.
Filter KETQUATHI directly by LANTHI = 1 and KQUA = 'Dat', then JOIN to HOCVIEN just to get the full name.
The question also asks to print "the subjects" passed on attempt 1 - so MAMH has to stay in the result, no GROUP BY or aggregation, each row is 1 (student, subject) pair that satisfies the condition.
QUESTION 4 (PART III) · JOIN
Exercise: Print the list of students (student ID, full name) in class "K11" who failed the CTRR subject (on attempt 1).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE hv.MALOP = N'K11' AND kq.MAMH = N'CTRR'
AND kq.LANTHI = 1 AND kq.KQUA = N'Khong Dat';Alternative approach (filtering by DIEM)
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE hv.MALOP = N'K11' AND kq.MAMH = N'CTRR'
AND kq.LANTHI = 1 AND kq.DIEM < 5;Same reasoning as Question 3 - DIEM < 5 and KQUA = 'Khong Dat' always go together thanks to the CHECK constraint from Question 4 (Part I), so both filters give the exact same result.
JOIN then filter on 4 conditions at once with AND: the right class, the right subject, the right attempt, the right result.
Only attempt 1 is in scope, so LANTHI = 1 must be filtered explicitly - without it, a student who retook the exam could still show up in the result even if they passed on a later attempt.
QUESTION 5 (PART III) · NOT EXISTS / NOT IN
Exercise: * List of students (student ID, full name) in class "K" who failed the CTRR subject (across all attempts).
SELECT DISTINCT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE hv.MALOP LIKE N'K%' AND kq.MAMH = N'CTRR'
AND NOT EXISTS (
SELECT * FROM KETQUATHI kq2
WHERE kq2.MAHV = hv.MAHV AND kq2.MAMH = N'CTRR' AND kq2.KQUA = N'Dat'
);Alternative approach (NOT IN)
SELECT DISTINCT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE hv.MALOP LIKE N'K%' AND kq.MAMH = N'CTRR'
AND hv.MAHV NOT IN (
SELECT MAHV FROM KETQUATHI WHERE MAMH = N'CTRR' AND KQUA = N'Dat'
);NOT IN is safe here specifically because KETQUATHI.MAHV is part of the primary key - it's never NULL, so the inner list can't fall into the NULL trap that would otherwise make NOT IN return nothing.
JOIN with KETQUATHI filters to students who actually took the CTRR exam (confirming they took it while also filtering by class); NOT EXISTS confirms none of their CTRR attempts passed - together this means every CTRR attempt by this student failed.
Class "K" here should be read as a class-code prefix - every class in this schema has a code starting with "K" (K11, K12, K13), so class "K" is equivalent to MALOP LIKE 'K%' (every class), not a single specific class - the same prefix-style phrasing as Question 3 in Week 2 ("product code starting with B"). If we only filtered KQUA = 'Khong Dat' like Question 4, a student who retook the exam and passed once would still show up incorrectly - NOT EXISTS is needed to guarantee no attempt ever passed, matching "across all attempts". DISTINCT avoids duplicate rows since JOIN KETQUATHI can match more than 1 CTRR attempt per student.
QUESTION 6 (PART III) · JOIN ... DISTINCT
Exercise: Find the names of the subjects taught by the teacher named "Tran Tam Thanh" in semester 1 of 2006.
SELECT DISTINCT mh.TENMH
FROM GIANGDAY gd
JOIN GIAOVIEN gv ON gd.MAGV = gv.MAGV
JOIN MONHOC mh ON gd.MAMH = mh.MAMH
WHERE gv.HOTEN = N'Tran Tam Thanh' AND gd.HOCKY = 1 AND gd.NAM = 2006;JOIN 3 tables GIANGDAY - GIAOVIEN - MONHOC to go from teacher name to subject name; DISTINCT because 1 teacher can teach the same subject to multiple classes in the same semester (duplicate rows).
The question only asks for the "subject name" (not the code), and per the sample data teacher GV02 teaches CTRR to both K11 and K12 in semester 1/2006 - DISTINCT avoids printing the subject name twice.
QUESTION 7 (PART III) · SUBQUERY
Exercise: Find the subjects (subject code, subject name) taught by the homeroom teacher of class "K11" in semester 1 of 2006.
SELECT DISTINCT mh.MAMH, mh.TENMH
FROM GIANGDAY gd
JOIN MONHOC mh ON gd.MAMH = mh.MAMH
WHERE gd.MAGV = (SELECT MAGVCN FROM LOP WHERE MALOP = N'K11')
AND gd.HOCKY = 1 AND gd.NAM = 2006;The subquery gets exactly 1 teacher code, the homeroom teacher of class K11, from LOP.MAGVCN, then filters GIANGDAY for that teacher and the right semester/year.
A plain equality subquery (=, not IN) is used because each class has exactly 1 homeroom teacher, so the subquery is guaranteed to return exactly 1 value - no need to join LOP into the outer query.
QUESTION 8 (PART III) · SUBQUERY ... IN
Exercise: Find the full name of the class monitor of the class(es) where the teacher named "Nguyen To Lan" teaches "Co So Du Lieu".
SELECT DISTINCT hv.HO + N' ' + hv.TEN AS HOTEN
FROM LOP l
JOIN HOCVIEN hv ON l.TRGLOP = hv.MAHV
WHERE l.MALOP IN (
SELECT gd.MALOP
FROM GIANGDAY gd
JOIN GIAOVIEN gv ON gd.MAGV = gv.MAGV
JOIN MONHOC mh ON gd.MAMH = mh.MAMH
WHERE gv.HOTEN = N'Nguyen To Lan' AND mh.TENMH = N'Co So Du Lieu'
);The subquery finds the class codes where teacher "Nguyen To Lan" teaches "Co So Du Lieu"; the outer query JOINs LOP with HOCVIEN via TRGLOP to get the class monitor's full name for those classes.
1 teacher can teach this subject to more than 1 class, so the subquery uses IN (not =) - DISTINCT guards against a duplicate monitor name if multiple GIANGDAY rows match the same class.
QUESTION 9 (PART III) · SUBQUERY
Exercise: Print the list of subjects (subject code, subject name) that must be completed as a prerequisite before "Co So Du Lieu".
SELECT mh.MAMH, mh.TENMH
FROM DIEUKIEN dk
JOIN MONHOC mh ON dk.MAMH_TRUOC = mh.MAMH
WHERE dk.MAMH = (SELECT MAMH FROM MONHOC WHERE TENMH = N'Co So Du Lieu');The subquery looks up the subject code for "Co So Du Lieu"; DIEUKIEN.MAMH_TRUOC lists the subjects that must be taken before it, then JOIN MONHOC gets their names.
The question gives a subject name, not a code, so the code must be looked up first via a subquery - hardcoding the subject code directly, even though we know it from the sample data, would break if the code ever changed, while looking it up by name stays correct.
QUESTION 10 (PART III) · SUBQUERY
Exercise: Which subjects (subject code, subject name) require "Cau Truc Roi Rac" as a mandatory prerequisite?
SELECT mh.MAMH, mh.TENMH
FROM DIEUKIEN dk
JOIN MONHOC mh ON dk.MAMH = mh.MAMH
WHERE dk.MAMH_TRUOC = (SELECT MAMH FROM MONHOC WHERE TENMH = N'Cau Truc Roi Rac');The reverse direction of Question 9 - this time filter by MAMH_TRUOC and pull out MAMH (the subjects that require CTRR as a prerequisite).
DIEUKIEN stores the pair (MAMH, MAMH_TRUOC); Question 9 goes from MAMH to find MAMH_TRUOC, Question 10 goes the other way, from MAMH_TRUOC to find MAMH - same table, filtered on a different column depending on the direction of the question.
QUESTION 11 (PART III) · SUBQUERY ... IN
Exercise: Find the full name of the teacher who taught CTRR to both class "K11" and class "K12" in the same semester 1 of 2006.
SELECT gv.HOTEN
FROM GIAOVIEN gv
WHERE gv.MAGV IN (
SELECT MAGV FROM GIANGDAY
WHERE MALOP = N'K11' AND MAMH = N'CTRR' AND HOCKY = 1 AND NAM = 2006
)
AND gv.MAGV IN (
SELECT MAGV FROM GIANGDAY
WHERE MALOP = N'K12' AND MAMH = N'CTRR' AND HOCKY = 1 AND NAM = 2006
);Alternative approach (INTERSECT)
(SELECT gv.HOTEN
FROM GIAOVIEN gv JOIN GIANGDAY gd ON gv.MAGV = gd.MAGV
WHERE gd.MAMH = N'CTRR' AND gd.HOCKY = 1 AND gd.NAM = 2006 AND gd.MALOP = N'K11')
INTERSECT
(SELECT gv.HOTEN
FROM GIAOVIEN gv JOIN GIANGDAY gd ON gv.MAGV = gd.MAGV
WHERE gd.MAMH = N'CTRR' AND gd.HOCKY = 1 AND gd.NAM = 2006 AND gd.MALOP = N'K12');INTERSECT keeps the overlap between 2 result sets - the list of teachers who taught K11 intersected with the list of teachers who taught K12 gives exactly the teachers who taught both classes, the same meaning as the 2 IN conditions joined by AND above, just expressed directly as a set operation.
2 independent subqueries, each returning the MAGV that taught CTRR to exactly 1 class (K11 or K12) in semester 1/2006; a valid teacher must satisfy both IN conditions at once.
You can't merge this into a single MALOP IN ('K11','K12') condition, since that would match a teacher who taught just 1 of the 2 classes - the 2 subqueries have to stay separate, joined by AND, to force the teacher to cover both classes.
QUESTION 12 (PART III) · NOT EXISTS
Exercise: Find the students (student ID, full name) who failed the CSDL subject on their 1st attempt but have not yet retaken it.
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.MAMH = N'CSDL' AND kq.LANTHI = 1 AND kq.KQUA = N'Khong Dat'
AND NOT EXISTS (
SELECT * FROM KETQUATHI kq2
WHERE kq2.MAHV = hv.MAHV AND kq2.MAMH = N'CSDL' AND kq2.LANTHI > 1
);The main condition filters for a failing CSDL attempt 1; NOT EXISTS checks that no row exists at all with LANTHI > 1 for that same student and subject.
"Has not retaken it" means no record exists from attempt 2 onward - LANTHI > 1 (rather than a fixed LANTHI = 2) is used to also safely catch the rare edge case of a student having a LANTHI = 3 record without a LANTHI = 2 one in the data (the schema has no constraint forcing attempt numbers to be contiguous) - NOT EXISTS is the right tool for checking "does not exist", as opposed to counting or filtering directly.
QUESTION 13 (PART III) · NOT EXISTS
Exercise: Find the teachers (teacher ID, full name) not assigned to teach any subject.
SELECT gv.MAGV, gv.HOTEN
FROM GIAOVIEN gv
WHERE NOT EXISTS (
SELECT * FROM GIANGDAY gd WHERE gd.MAGV = gv.MAGV
);Alternative approach (LEFT JOIN)
SELECT gv.MAGV, gv.HOTEN
FROM GIAOVIEN gv LEFT JOIN GIANGDAY gd ON gv.MAGV = gd.MAGV
WHERE gd.MAGV IS NULL;LEFT JOIN keeps every teacher even when no GIANGDAY row matches (the GIANGDAY-side columns come back NULL); filtering WHERE gd.MAGV IS NULL is the classic "anti-join" pattern for finding rows with no match, giving the same result as NOT EXISTS.
NOT EXISTS checks that the teacher doesn't appear in any GIANGDAY row at all.
This is a total negation query ("not any") - NOT EXISTS is always the safest choice here, avoiding the NULL trap that NOT IN can fall into.
QUESTION 14 (PART III) · NOT EXISTS
Exercise: Find the teachers (teacher ID, full name) not assigned to teach any subject belonging to the department they head.
SELECT gv.MAGV, gv.HOTEN
FROM GIAOVIEN gv
WHERE NOT EXISTS (
SELECT * FROM GIANGDAY gd
JOIN MONHOC mh ON gd.MAMH = mh.MAMH
WHERE gd.MAGV = gv.MAGV AND mh.MAKHOA = gv.MAKHOA
);Alternative approach (LEFT JOIN)
SELECT gv.MAGV, gv.HOTEN
FROM GIAOVIEN gv
LEFT JOIN GIANGDAY gd ON gv.MAGV = gd.MAGV
LEFT JOIN MONHOC mh ON gd.MAMH = mh.MAMH AND mh.MAKHOA = gv.MAKHOA
WHERE mh.MAMH IS NULL;Same anti-join technique as Question 13, but the "belongs to the teacher's department" condition (mh.MAKHOA = gv.MAKHOA) has to sit inside the ON clause of LEFT JOIN MONHOC (not in WHERE) - putting it in WHERE would wrongly drop the non-matching rows that LEFT JOIN is meant to preserve (where MONHOC is already NULL) before the IS NULL condition even gets a chance to evaluate them.
NOT EXISTS checks that there's no GIANGDAY row for that teacher matching a subject that belongs to the teacher's own department (mh.MAKHOA = gv.MAKHOA).
Unlike Question 13 (not teaching anything at all), this only cares about subjects from the teacher's own department - a teacher can still teach subjects from other departments and satisfy this condition, as long as they teach nothing from their own department.
QUESTION 15 (PART III) · EXISTS ... OR
Exercise: Find the full names of students in class "K11" who either failed any subject on more than 3 attempts and are still "Khong dat", or scored exactly 5 on their 2nd attempt at CTRR.
SELECT DISTINCT hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE hv.MALOP = N'K11'
AND (
EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV AND kq.LANTHI = 3 AND kq.KQUA = N'Khong Dat'
)
OR EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV AND kq.MAMH = N'CTRR' AND kq.LANTHI = 2 AND kq.DIEM = 5
)
);2 EXISTS branches joined by OR inside the same parentheses: branch 1 checks for any subject that reached attempt 3 (the maximum) and still failed; branch 2 checks for a CTRR attempt 2 scoring exactly 5.
"Failed any subject on more than 3 attempts" - given the max LANTHI is 3 (Part I, Question 5) - is read as having reached attempt 3 (the last allowed attempt) and still being "Khong dat", meaning all retake chances are exhausted; this is the closest reading consistent with the schema's actual data constraint. Watch out: Writing this condition literally as LANTHI > 3 would make that branch always empty (never true), since the CHECK from Question 5 (Part I) already caps LANTHI at 3 - "failed on more than 3 attempts" has to be read according to how the schema's constraint actually works (all 3 allowed attempts used up), not translated word-for-word into > 3.
QUESTION 16 (PART III) · GROUP BY ... HAVING
Exercise: Find the full name of the teacher who taught CTRR to at least 2 classes in the same semester of the same academic year.
SELECT gv.HOTEN
FROM GIAOVIEN gv
WHERE gv.MAGV IN (
SELECT MAGV
FROM GIANGDAY
WHERE MAMH = N'CTRR'
GROUP BY MAGV, HOCKY, NAM
HAVING COUNT(DISTINCT MALOP) >= 2
);GROUP BY (MAGV, HOCKY, NAM), then count distinct classes with COUNT(DISTINCT MALOP); HAVING >= 2 keeps only groups teaching 2 or more classes.
Both HOCKY and NAM must be in the GROUP BY (not just MAGV), since the question requires "in the same semester of the same academic year" - grouping by MAGV alone would wrongly count a teacher who taught K11 in semester 1 and K12 in semester 2 (2 different semesters) as satisfying the condition. Note: Group by MAGV (the primary key) and only pull HOTEN out afterward - grouping directly by HOTEN instead of MAGV would accidentally merge 2 teachers who happen to share the same name into a single group, skewing the count.
QUESTION 17 (PART III) · SUBQUERY ... MAX
Exercise: List of students and their CSDL exam scores (using only the score from the most recent attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, kq.DIEM
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.MAMH = N'CSDL'
AND kq.LANTHI = (
SELECT MAX(kq2.LANTHI) FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = N'CSDL'
);The MAX(LANTHI) subquery finds each student's most recent attempt for the CSDL subject; the outer condition keeps only the row matching that attempt.
A student can retake the CSDL exam multiple times - without filtering to the most recent attempt, scores from earlier attempts would also print, which contradicts what the question asks for.
QUESTION 18 (PART III) · GROUP BY ... MAX
Exercise: List of students and their "Co So Du Lieu" exam scores (using the highest score across all attempts).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, MAX(kq.DIEM) AS DIEM
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.MAMH = (SELECT MAMH FROM MONHOC WHERE TENMH = N'Co So Du Lieu')
GROUP BY hv.MAHV, hv.HO, hv.TEN;GROUP BY per student, then MAX(DIEM) - unlike Question 17 (filtering by most recent attempt), this takes the highest score regardless of which attempt it came from.
"Highest score across all attempts" is a different requirement from "score from the most recent attempt" in Question 17 - a student could score their highest on the first try and then score lower on a retake; MAX(DIEM) always picks the highest value regardless of attempt order.
QUESTION 19 (PART III) · SUBQUERY ... MIN
Exercise: Which department (department code, department name) was established earliest?
SELECT MAKHOA, TENKHOA
FROM KHOA
WHERE NGTLAP = (SELECT MIN(NGTLAP) FROM KHOA);The MIN(NGTLAP) subquery finds the earliest founding date; the outer query keeps the department(s) whose founding date matches it.
ORDER BY with TOP 1 isn't used because if 2 departments share the same earliest date, TOP 1 would drop 1 of them - comparing directly against MIN(...) always keeps every tied department.
QUESTION 20 (PART III) · COUNT
Exercise: How many teachers hold the academic rank "GS" or "PGS"?
SELECT COUNT(*) AS SoLuong
FROM GIAOVIEN
WHERE HOCHAM IN (N'GS', N'PGS');COUNT(*) counts the rows matching either of the 2 listed values for HOCHAM.
HOCHAM can be NULL (a teacher with no academic rank yet) - IN (...) automatically skips NULL rows without needing a separate IS NOT NULL condition.
QUESTION 21 (PART III) · GROUP BY
Exercise: Report how many teachers hold the degree "CN", "KS", "Ths", "TS", "PTS" within each department.
SELECT MAKHOA, HOCVI, COUNT(*) AS SoLuong
FROM GIAOVIEN
WHERE HOCVI IN (N'CN', N'KS', N'Ths', N'TS', N'PTS')
GROUP BY MAKHOA, HOCVI;GROUP BY on the pair (MAKHOA, HOCVI), COUNT(*) counts teachers within each group.
Both columns must be in GROUP BY together since the report needs a breakdown down to each (department, degree) combination - grouping by MAKHOA alone would merge every degree together into 1 row.
QUESTION 22 (PART III) · GROUP BY
Exercise: For each subject, report the number of students by result (passed and failed).
SELECT MAMH, KQUA, COUNT(*) AS SoLuong
FROM KETQUATHI
GROUP BY MAMH, KQUA;Alternative approach (pivot with CASE WHEN)
SELECT MAMH,
COUNT(CASE WHEN KQUA = N'Dat' THEN 1 END) AS SoLuongDat,
COUNT(CASE WHEN KQUA = N'Khong Dat' THEN 1 END) AS SoLuongKhongDat
FROM KETQUATHI
GROUP BY MAMH;COUNT(CASE WHEN ... THEN 1 END) only counts the rows matching the condition inside CASE (non-matching rows return NULL, and COUNT always skips NULL) - this technique "pivots" the result from multiple rows (1 per KQUA value) into a single row per subject with 2 separate columns, easier to read side by side when comparing passed vs failed.
GROUP BY on the pair (MAMH, KQUA) to count attempts by result for each subject.
This counts by exam attempt (each KETQUATHI row), without deduplicating students who retook the exam - since every row in KETQUATHI represents exactly 1 attempt, counting rows directly is the most reasonable reading; a student who retook a subject could contribute to both the "Khong Dat" group (earlier attempt) and the "Dat" group (later attempt) for the same subject.
QUESTION 23 (PART III) · EXISTS
Exercise: Find the teachers (teacher ID, full name) who are the homeroom teacher of a class and also teach at least one subject to that same class.
SELECT gv.MAGV, gv.HOTEN
FROM GIAOVIEN gv
JOIN LOP l ON gv.MAGV = l.MAGVCN
WHERE EXISTS (
SELECT * FROM GIANGDAY gd WHERE gd.MAGV = gv.MAGV AND gd.MALOP = l.MALOP
);JOIN GIAOVIEN with LOP via MAGVCN to find which class each teacher is homeroom teacher of; EXISTS checks whether that teacher teaches any subject to that exact class.
The class must match exactly between GIANGDAY and the LOP row being considered (gd.MALOP = l.MALOP) - just checking "teaches something, somewhere" without tying it to the right class would wrongly count a homeroom teacher who teaches a different class entirely.
QUESTION 24 (PART III) · SUBQUERY ... MAX
Exercise: Find the full name of the class monitor of the class with the largest enrollment.
SELECT hv.HO + N' ' + hv.TEN AS HOTEN
FROM LOP l
JOIN HOCVIEN hv ON l.TRGLOP = hv.MAHV
WHERE l.SISO = (SELECT MAX(SISO) FROM LOP);The MAX(SISO) subquery finds the largest enrollment; the outer query JOINs LOP with HOCVIEN via TRGLOP to get the monitor's full name for that class.
This approach automatically keeps every class tied for the largest enrollment instead of dropping some, the way TOP 1 would - same reasoning as Question 19.
QUESTION 25 (PART III) · SCALAR SUBQUERY
Exercise: * Find the full names of class monitors (LOPTRG) who failed more than 3 subjects (each subject failed on every attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE hv.MAHV IN (SELECT TRGLOP FROM LOP)
AND (
SELECT COUNT(DISTINCT kq.MAMH)
FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV
AND NOT EXISTS (
SELECT * FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = kq.MAMH AND kq2.KQUA = N'Dat'
)
) > 3;The subquery counts COUNT(DISTINCT MAMH), where for each subject NOT EXISTS confirms no attempt ever passed (same idea as Question 5) - the result is the number of subjects this student has never passed despite taking them.
hv.MAHV IN (SELECT TRGLOP FROM LOP) filters to exactly the students currently acting as a class monitor (LOPTRG) for any class; the > 3 count condition sits in its own parentheses because it's a scalar subquery compared directly, not a HAVING clause (since the outer query has no GROUP BY).
QUESTION 26 (PART III) · GROUP BY ... TOP WITH TIES
Exercise: Find the student (student ID, full name) with the most subjects scored 9 or 10.
SELECT TOP 1 WITH TIES hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.DIEM IN (9, 10)
GROUP BY hv.MAHV, hv.HO, hv.TEN
ORDER BY COUNT(*) DESC;GROUP BY per student, counting scores of 9 or 10; TOP 1 WITH TIES keeps every student tied for the top count instead of just 1 row.
TOP ... WITH TIES (not a plain TOP 1) is needed because multiple students could tie for the highest count of 9-10 scores - a plain TOP 1 would arbitrarily keep just 1 of them.
QUESTION 27 (PART III) · CORRELATED SUBQUERY ... MAX
Exercise: Within each class, find the student (student ID, full name) with the most subjects scored 9 or 10.
SELECT t.MALOP, t.MAHV, t.HOTEN, t.SoMon
FROM (
SELECT hv.MALOP, hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, COUNT(*) AS SoMon
FROM HOCVIEN hv
JOIN KETQUATHI kq ON hv.MAHV = kq.MAHV
WHERE kq.DIEM IN (9, 10)
GROUP BY hv.MALOP, hv.MAHV, hv.HO, hv.TEN
) t
WHERE t.SoMon = (
SELECT MAX(t2.SoMon)
FROM (
SELECT hv2.MALOP, hv2.MAHV, COUNT(*) AS SoMon
FROM HOCVIEN hv2
JOIN KETQUATHI kq2 ON hv2.MAHV = kq2.MAHV
WHERE kq2.DIEM IN (9, 10)
GROUP BY hv2.MALOP, hv2.MAHV
) t2
WHERE t2.MALOP = t.MALOP
);The derived table t counts each student's 9-10 scores; the derived table t2 recomputes the same figures and takes MAX for the matching class (t2.MALOP = t.MALOP), comparing within each class separately.
Unlike Question 26 (school-wide comparison), this needs the highest count computed separately per class - a single shared MAX value like Question 19/24 won't work, so MAX must be correlated to the exact MALOP being evaluated.
QUESTION 28 (PART III) · GROUP BY ... COUNT DISTINCT
Exercise: For each semester of each academic year, report how many subjects and how many classes each teacher was assigned to teach.
SELECT MAGV, HOCKY, NAM, COUNT(DISTINCT MAMH) AS SoMonHoc, COUNT(DISTINCT MALOP) AS SoLop
FROM GIANGDAY
GROUP BY MAGV, HOCKY, NAM;GROUP BY (MAGV, HOCKY, NAM); COUNT(DISTINCT MAMH) counts distinct subjects, COUNT(DISTINCT MALOP) counts distinct classes within each group.
DISTINCT inside COUNT is required because a teacher can appear in multiple GIANGDAY rows for different classes but the same subject (or vice versa) within the same semester/year - a plain COUNT(*) would just count assignment rows, not the actual number of subjects/classes.
QUESTION 29 (PART III) · CORRELATED SUBQUERY ... MAX
Exercise: For each semester of each academic year, find the teacher (teacher ID, full name) who taught the most.
SELECT t.MAGV, gv.HOTEN, t.HOCKY, t.NAM, t.SoLuot
FROM (
SELECT MAGV, HOCKY, NAM, COUNT(*) AS SoLuot
FROM GIANGDAY
GROUP BY MAGV, HOCKY, NAM
) t
JOIN GIAOVIEN gv ON t.MAGV = gv.MAGV
WHERE t.SoLuot = (
SELECT MAX(t2.SoLuot)
FROM (
SELECT MAGV, COUNT(*) AS SoLuot
FROM GIANGDAY
WHERE HOCKY = t.HOCKY AND NAM = t.NAM
GROUP BY MAGV
) t2
);The derived table t counts each teacher's number of assignments (GIANGDAY rows) per (semester, year); it's compared against the MAX for that exact semester/year (correlated via HOCKY, NAM) to keep the top teacher within each group.
Like Question 27, "the most" here means the most within each semester/year individually - not the most across the entire dataset, so MAX must be correlated to the exact (HOCKY, NAM) being evaluated.
QUESTION 30 (PART III) · GROUP BY ... TOP WITH TIES
Exercise: Find the subject (subject code, subject name) with the highest number of students who failed (on the 1st attempt).
SELECT TOP 1 WITH TIES mh.MAMH, mh.TENMH
FROM MONHOC mh
JOIN KETQUATHI kq ON mh.MAMH = kq.MAMH
WHERE kq.LANTHI = 1 AND kq.KQUA = N'Khong Dat'
GROUP BY mh.MAMH, mh.TENMH
ORDER BY COUNT(*) DESC;GROUP BY per subject, counting attempt-1 failures; TOP 1 WITH TIES keeps every subject tied for the highest count.
Same reasoning as Question 26 - TOP ... WITH TIES avoids dropping a subject if multiple subjects tie for the most attempt-1 failures.
QUESTION 31 (PART III) · EXISTS / NOT EXISTS
Exercise: Find students (student ID, full name) who passed every subject they took (considering only the 1st attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE EXISTS (
SELECT * FROM KETQUATHI kq WHERE kq.MAHV = hv.MAHV AND kq.LANTHI = 1
)
AND NOT EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV AND kq.LANTHI = 1 AND kq.KQUA = N'Khong Dat'
);EXISTS confirms the student has at least 1 attempt-1 record; NOT EXISTS confirms none of their attempt-1 records failed - together this means every subject's first attempt was a pass.
Both EXISTS and NOT EXISTS are needed - with only NOT EXISTS, a student who never took any exam at all would vacuously satisfy "never failed", even though they never took a subject in the first place.
QUESTION 32 (PART III) · EXISTS / NOT EXISTS
Exercise: * Find students (student ID, full name) who passed every subject they took (considering only the most recent attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE EXISTS (
SELECT * FROM KETQUATHI kq WHERE kq.MAHV = hv.MAHV
)
AND NOT EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV
AND kq.KQUA = N'Khong Dat'
AND kq.LANTHI = (
SELECT MAX(kq2.LANTHI) FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = kq.MAMH
)
);This time NOT EXISTS checks that no subject's most recent attempt (MAX(LANTHI) for that subject) failed - unlike Question 31, which only looked at exactly LANTHI = 1.
The "most recent attempt" differs by subject and by student (some subjects taken once, others retaken 2-3 times) - MAX(LANTHI) must be recomputed correlated to each (MAHV, MAMH) pair, the same technique used in Question 17, rather than a fixed LANTHI value like Question 31.
QUESTION 33 (PART III) · DOUBLE NOT EXISTS
Exercise: * Find students (student ID, full name) who passed all subjects taken (considering only the 1st attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE NOT EXISTS (
SELECT mh.MAMH
FROM MONHOC mh
WHERE NOT EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV AND kq.MAMH = mh.MAMH AND kq.LANTHI = 1 AND kq.KQUA = N'Dat'
)
);This is the classic relational division pattern using 2 nested NOT EXISTS - "there is no subject the student did not pass on attempt 1" is equivalent to "every subject was passed on attempt 1".
Unlike Question 31 (only considering subjects the student actually took), this requires the student to have taken every single subject that exists in MONHOC - the "not exists X such that not exists Y" structure is the standard SQL pattern for a "for all" condition, since SQL has no direct universal quantifier.
QUESTION 34 (PART III) · DOUBLE NOT EXISTS
Exercise: * Find students (student ID, full name) who passed all subjects taken (considering only the most recent attempt).
SELECT hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN
FROM HOCVIEN hv
WHERE NOT EXISTS (
SELECT mh.MAMH
FROM MONHOC mh
WHERE NOT EXISTS (
SELECT * FROM KETQUATHI kq
WHERE kq.MAHV = hv.MAHV AND kq.MAMH = mh.MAMH AND kq.KQUA = N'Dat'
AND kq.LANTHI = (
SELECT MAX(kq2.LANTHI) FROM KETQUATHI kq2
WHERE kq2.MAHV = hv.MAHV AND kq2.MAMH = mh.MAMH
)
)
);The same division structure as Question 33, but the inner "passed" condition uses each subject's most recent attempt instead of a fixed LANTHI = 1.
This combines the "most recent attempt" technique (Question 17, Question 32) with the division technique (Question 33) - the student must have passed the latest attempt of every subject that exists to qualify.
QUESTION 35 (PART III) · CORRELATED SUBQUERY ... MAX
Exercise: ** Find the student (student ID, full name) with the highest score in each subject (using the most recent attempt's score).
SELECT t.MAMH, mh.TENMH, t.MAHV, t.HOTEN, t.Diem
FROM (
SELECT kq.MAMH, hv.MAHV, hv.HO + N' ' + hv.TEN AS HOTEN, kq.DIEM AS Diem
FROM KETQUATHI kq
JOIN HOCVIEN hv ON kq.MAHV = hv.MAHV
WHERE kq.LANTHI = (
SELECT MAX(kq2.LANTHI) FROM KETQUATHI kq2
WHERE kq2.MAHV = kq.MAHV AND kq2.MAMH = kq.MAMH
)
) t
JOIN MONHOC mh ON t.MAMH = mh.MAMH
WHERE t.Diem = (
SELECT MAX(t2.Diem)
FROM (
SELECT kq3.MAMH, kq3.DIEM AS Diem
FROM KETQUATHI kq3
WHERE kq3.LANTHI = (
SELECT MAX(kq4.LANTHI) FROM KETQUATHI kq4
WHERE kq4.MAHV = kq3.MAHV AND kq4.MAMH = kq3.MAMH
)
) t2
WHERE t2.MAMH = t.MAMH
);The derived table t builds the list of scores from the most recent attempt of each (student, subject) pair - the same technique as Question 17; the result is then filtered to rows whose score equals the highest score for that specific subject (t2.MAMH = t.MAMH).
This is a "maximum per group" problem (like Question 27, Question 29) applied on top of data already filtered to the most recent attempt (like Question 17) - both techniques have to be combined: correlating MAX by MAMH, while also excluding any attempt that isn't the most recent one.
