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 · PART III - DATA QUERY LANGUAGE
Sales Management - Questions 1 ➜ 25
Use SQL statements in SQL Server Management Studio to carry out the 25 queries below on the Sales Management schema. Attempt each one yourself before checking the answer.
<MSSV>_<HoVaTen>_BTTH2.sql (MSSV is your student ID, HoVaTen is your full name).- Print the list of products (MASP, TENSP) manufactured in "Trung Quoc".
- Print the list of products (MASP, TENSP) whose unit of measure is "cay" or "quyen".
- Print the list of products (MASP, TENSP) whose product code starts with "B" and ends with "01".
- Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" priced from 30,000 to 40,000.
- Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" or "Thai Lan" priced from 30,000 to 40,000.
- Print the invoice numbers and invoice totals sold on 1/1/2007 and 2/1/2007.
- Print the invoice numbers and invoice totals in January 2007, sorted by date (ascending) and invoice total (descending).
- Print the list of customers (MAKH, HOTEN) who made a purchase on 1/1/2007.
- Print the invoice numbers and invoice totals for invoices created by the employee named "Nguyen Van B" on 28/10/2006.
- Print the list of products (MASP, TENSP) purchased by the customer named "Nguyen Van A" during October 2006.
- Find the invoice numbers that include the product with code "BB01" or "BB02".
- Find the invoice numbers that include the product with code "BB01" or "BB02", each purchased in a quantity from 10 to 20.
- Find the invoice numbers that include both products with code "BB01" and "BB02" at the same time, each purchased in a quantity from 10 to 20.
- Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" or products sold on 1/1/2007.
- Print the list of products (MASP, TENSP) that have never been sold.
- Print the list of products (MASP, TENSP) that were not sold in 2006.
- Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" that were not sold in 2006.
- Report the number of invoices created by each employee in 2006, showing (MANV, HOTEN, SoLuongHD).
- Print the list of employees together with the total number of distinct customers they sold to in 2006.
- List the product(s) (MASP, TENSP) with the highest total quantity sold in 2006.
- Find the employee with the highest sales revenue in October 2006.
- Print the list of products not sold in 2007 but sold in 2006.
- List the products (MASP, TENSP) sold by at least 2 different employees.
- Print the list of customers who did not purchase any product manufactured in Thailand.
- Find the invoice with the highest total value in 2006, printing (SOHD, NGHD, TRIGIA).
ANSWER
View answer
Protected content
Enter the access code to view the Week 2 answer.
Access code provided by the instructor.
Full answer key for Questions 1 ➜ 25. Retype each query in SSMS so it actually sticks - the Copy button here is just for show.
QUESTION 1 · WHERE
Exercise: Print the list of products (MASP, TENSP) manufactured in "Trung Quoc".
SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX = 'Trung Quoc';WHERE filters on a single equality condition on the NUOCSX column.
The exercise only asks for a single condition, so an equality comparison is enough - no more complex technique is needed.
QUESTION 2 · IN
Exercise: Print the list of products (MASP, TENSP) whose unit of measure is "cay" or "quyen".
SELECT MASP, TENSP
FROM SANPHAM
WHERE DVT IN ('cay', 'quyen');IN checks whether a value is present in a given list.
IN is more compact when comparing 1 column to several discrete values - equivalent to but more readable than a chain of ORs.
Alternative approach
SELECT MASP, TENSP
FROM SANPHAM
WHERE DVT = 'cay' OR DVT = 'quyen';QUESTION 3 · LIKE
Exercise: Print the list of products (MASP, TENSP) whose product code starts with "B" and ends with "01".
SELECT MASP, TENSP
FROM SANPHAM
WHERE MASP LIKE 'B%01';LIKE matches a string pattern; % stands in for any sequence of characters (including none) between "B" and "01".
The exercise requires a condition on the first/last characters, not an exact equality match, so LIKE must be used instead of =.
QUESTION 4 · BETWEEN
Exercise: Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" priced from 30,000 to 40,000.
SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX = 'Trung Quoc' AND (GIA BETWEEN 30000 AND 40000);BETWEEN a AND b is equivalent to >= a AND <= b, checking whether a value falls within a closed range.
BETWEEN is more concise when filtering a continuous range like GIA from 30,000 to 40,000.
Alternative approach
SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX = 'Trung Quoc' AND (GIA >= 30000 AND GIA <= 40000);QUESTION 5 · IN + BETWEEN
Exercise: Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" or "Thai Lan" priced from 30,000 to 40,000.
SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX IN ('Trung Quoc', 'Thai Lan') AND (GIA BETWEEN 30000 AND 40000);Combines IN (multiple countries of manufacture) and BETWEEN (price range) with AND.
The 2 independent conditions (country of manufacture and price range) must both be true at the same time, so they're joined with AND; each condition is itself shortened with IN/BETWEEN instead of a longer OR/comparison chain.
QUESTION 6 · IN by date
Exercise: Print the invoice numbers and invoice totals sold on 1/1/2007 and 2/1/2007.
SELECT SOHD, TRIGIA
FROM HOADON
WHERE NGHD IN ('1/1/2007', '2/1/2007');Compares the NGHD column (date type) against a list of 2 specific date values.
IN replaces OR when comparing the same column against several discrete values - this applies to date columns too, not just strings or numbers.
QUESTION 7 · YEAR/MONTH + multi-column ORDER BY
Exercise: Print the invoice numbers and invoice totals in January 2007, sorted by date (ascending) and invoice total (descending).
SELECT SOHD, NGHD, TRIGIA
FROM HOADON
WHERE YEAR(NGHD) = 2007 AND MONTH(NGHD) = 1
ORDER BY NGHD ASC, TRIGIA DESC;YEAR()/MONTH() extract the year/month from a date value; ORDER BY can sort by multiple columns, each with its own ascending/descending direction.
Filtering "January 2007" requires splitting out the year and month since NGHD stores a full date. Sorting by 2 criteria in opposite directions (date ascending, total descending) means ASC/DESC must be declared separately for each column within the same ORDER BY.
QUESTION 8 · JOIN
Exercise: Print the list of customers (MAKH, HOTEN) who made a purchase on 1/1/2007.
SELECT hd.MAKH, HOTEN
FROM KHACHHANG kh JOIN HOADON hd ON kh.MAKH = hd.MAKH
WHERE NGHD = '1/1/2007';JOIN combines KHACHHANG with HOADON through MAKH to get the HOTEN of customers who have an invoice on that exact date.
The data needed (HOTEN) lives in KHACHHANG, while the filter condition (NGHD) lives in HOADON - the 2 tables must be JOINed to query both at once.
Alternative approach
SELECT MAKH, HOTEN
FROM KHACHHANG
WHERE MAKH IN (SELECT MAKH
FROM HOADON
WHERE NGHD = '1/1/2007');QUESTION 9 · JOIN filtered by employee name
Exercise: Print the invoice numbers and invoice totals for invoices created by the employee named "Nguyen Van B" on 28/10/2006.
SELECT SOHD, TRIGIA
FROM HOADON hd JOIN NHANVIEN nv ON hd.MANV = nv.MANV
WHERE HOTEN = 'Nguyen Van B' AND NGHD = '28/10/2006';JOIN HOADON with NHANVIEN through MANV to filter by employee full name.
HOADON only stores MANV (the code), not the employee's name - a JOIN to NHANVIEN is required before filtering by the name "Nguyen Van B".
Alternative approach
SELECT SOHD, TRIGIA
FROM HOADON
WHERE NGHD = '28/10/2006' AND MANV IN (SELECT MANV
FROM NHANVIEN
WHERE HOTEN = 'Nguyen Van B');QUESTION 10 · JOIN 4 tables
Exercise: Print the list of products (MASP, TENSP) purchased by the customer named "Nguyen Van A" during October 2006.
SELECT sp.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
JOIN KHACHHANG kh ON kh.MAKH = hd.MAKH
WHERE HOTEN = 'Nguyen Van A' AND YEAR(NGHD) = 2006 AND MONTH(NGHD) = 10;Chains 4 tables together (SANPHAM ➜ CTHD ➜ HOADON ➜ KHACHHANG) following the exact foreign-key chain to get from "customer name" to "products purchased".
There's no direct foreign key between KHACHHANG and SANPHAM - you have to go through HOADON and CTHD (the intermediate tables) to connect 2 tables that are 2 relationship layers apart.
Alternative approach (nested subquery)
SELECT MASP, TENSP
FROM SANPHAM
WHERE MASP IN (SELECT MASP
FROM CTHD
WHERE SOHD IN (SELECT SOHD
FROM HOADON
WHERE YEAR(NGHD) = 2006 AND MONTH(NGHD) = 10
AND MAKH IN (SELECT MAKH
FROM KHACHHANG
WHERE HOTEN = 'Nguyen Van A')));QUESTION 11 · DISTINCT + IN
Exercise: Find the invoice numbers that include the product with code "BB01" or "BB02".
SELECT DISTINCT SOHD
FROM CTHD
WHERE MASP IN ('BB01', 'BB02');DISTINCT removes duplicate SOHD values from the returned result.
An invoice could include both BB01 and BB02 (2 CTHD rows) - without DISTINCT, that invoice would appear twice in the result.
Alternative approach (UNION)
(SELECT SOHD FROM CTHD WHERE MASP = 'BB01')
UNION
(SELECT SOHD FROM CTHD WHERE MASP = 'BB02');QUESTION 12 · Adding a quantity condition
Exercise: Find the invoice numbers that include the product with code "BB01" or "BB02", each purchased in a quantity from 10 to 20.
SELECT DISTINCT SOHD
FROM CTHD
WHERE MASP IN ('BB01', 'BB02') AND (SL BETWEEN 10 AND 20);Adds the condition AND (SL BETWEEN 10 AND 20), applied to each CTHD row that matches MASP.
The quantity condition applies to the individual line item under consideration (not a summed total), so it only needs an extra AND in the same WHERE - no GROUP BY is needed.
QUESTION 13 · INTERSECT
Exercise: Find the invoice numbers that include both products with code "BB01" and "BB02" at the same time, each purchased in a quantity from 10 to 20.
(SELECT SOHD FROM CTHD WHERE MASP = 'BB01' AND (SL BETWEEN 10 AND 20))
INTERSECT
(SELECT SOHD FROM CTHD WHERE MASP = 'BB02' AND (SL BETWEEN 10 AND 20));INTERSECT returns the rows that appear in both result sets (the intersection of the 2 SOHD sets).
Questions 11/12 use "or" (IN) because only 1 of the 2 products is needed; this question requires "both at the same time", so the intersection must be taken with INTERSECT - IN/OR cannot express this requirement.
QUESTION 14 · UNION
Exercise: Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" or products sold on 1/1/2007.
(SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX = 'Trung Quoc')
UNION
(SELECT sp.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
WHERE NGHD = '1/1/2007');UNION combines 2 independent result sets (products by country of manufacture, products by sale date) and automatically removes duplicates.
The 2 conditions belong to 2 different logical branches (one only needs SANPHAM, the other needs to JOIN in CTHD/HOADON), so they're split into 2 separate SELECT statements and combined with UNION rather than being merged into 1 query.
QUESTION 15 · NOT IN
Exercise: Print the list of products (MASP, TENSP) that have never been sold.
SELECT MASP, TENSP
FROM SANPHAM
WHERE MASP NOT IN (SELECT DISTINCT MASP FROM CTHD);NOT IN excludes any MASP that has ever appeared in CTHD (i.e., has ever been sold).
"Never sold" means the MASP doesn't exist in CTHD - the negation of IN is NOT IN.
Alternative approach (EXCEPT)
(SELECT MASP, TENSP FROM SANPHAM)
EXCEPT
(SELECT DISTINCT ct.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP);QUESTION 16 · NOT IN nested through HOADON
Exercise: Print the list of products (MASP, TENSP) that were not sold in 2006.
SELECT MASP, TENSP
FROM SANPHAM
WHERE MASP NOT IN (SELECT DISTINCT MASP
FROM CTHD
WHERE SOHD IN (SELECT SOHD
FROM HOADON
WHERE YEAR(NGHD) = 2006));A 2-layer nested subquery: the inner layer filters SOHD by year 2006, the outer layer filters MASP by those SOHD values.
CTHD has no year column - it must be nested through HOADON (which has NGHD) first to determine which CTHD rows belong to 2006.
Alternative approach (EXCEPT)
(SELECT MASP, TENSP FROM SANPHAM)
EXCEPT
(SELECT DISTINCT ct.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON ct.SOHD = hd.SOHD
WHERE YEAR(NGHD) = 2006);QUESTION 17 · Combining a condition with Question 16
Exercise: Print the list of products (MASP, TENSP) manufactured in "Trung Quoc" that were not sold in 2006.
SELECT MASP, TENSP
FROM SANPHAM
WHERE NUOCSX = 'Trung Quoc'
AND MASP NOT IN (SELECT DISTINCT MASP
FROM CTHD
WHERE SOHD IN (SELECT SOHD
FROM HOADON
WHERE YEAR(NGHD) = 2006));Combines a NUOCSX condition (on SANPHAM) with the same NOT IN subquery as Question 16.
This is Question 16 narrowed further with 1 more condition (country of manufacture) via AND - reusing the same subquery logic already built instead of writing it from scratch.
QUESTION 18 · GROUP BY + COUNT
Exercise: Report the number of invoices created by each employee in 2006, showing (MANV, HOTEN, SoLuongHD).
SELECT nv.MANV, HOTEN, COUNT(*) AS SoLuongHD
FROM NHANVIEN nv JOIN HOADON hd ON nv.MANV = hd.MANV
WHERE YEAR(NGHD) = 2006
GROUP BY nv.MANV, HOTEN;GROUP BY groups invoices by each employee; COUNT(*) counts the number of rows (invoices) in each group.
The exercise asks for figures "per employee" (not an overall total), so grouping by MANV is required (along with HOTEN, which is functionally dependent on MANV).
QUESTION 19 · COUNT DISTINCT
Exercise: Print the list of employees together with the total number of distinct customers they sold to in 2006.
SELECT nv.MANV, nv.HOTEN, COUNT(DISTINCT hd.MAKH) AS TongSoKH
FROM NHANVIEN nv JOIN HOADON hd ON nv.MANV = hd.MANV
JOIN KHACHHANG kh ON kh.MAKH = hd.MAKH
WHERE YEAR(NGHD) = 2006
GROUP BY nv.MANV, nv.HOTEN;COUNT(DISTINCT ...) counts the number of distinct values, ignoring duplicates.
An employee may have sold multiple invoices to the same customer - a plain COUNT(*) would count that customer multiple times, so DISTINCT is needed to count each customer once.
QUESTION 20 · SUM + TOP WITH TIES
Exercise: List the product(s) (MASP, TENSP) with the highest total quantity sold in 2006.
SELECT TOP 1 WITH TIES sp.MASP, TENSP, SUM(SL) AS TongSoLuong
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
WHERE YEAR(NGHD) = 2006
GROUP BY sp.MASP, TENSP
ORDER BY TongSoLuong DESC;SUM(SL) adds up the quantity for each product; TOP 1 WITH TIES takes the top row and also keeps any tied rows.
SUM is required because the exercise asks for "total quantity sold", not the number of sales. WITH TIES is used so you don't miss the case where multiple products are tied for the highest total.
QUESTION 21 · SUM of value
Exercise: Find the employee with the highest sales revenue in October 2006.
SELECT TOP 1 WITH TIES nv.MANV, HOTEN, SUM(TRIGIA) AS TongDoanhSo
FROM NHANVIEN nv JOIN HOADON hd ON nv.MANV = hd.MANV
WHERE MONTH(NGHD) = 10 AND YEAR(NGHD) = 2006
GROUP BY nv.MANV, HOTEN
ORDER BY TongDoanhSo DESC;SUM(TRIGIA) adds up the total value of the invoices each employee created in October 2006.
"Sales" means the total invoice value an employee generated, which differs from Question 18 (counting the number of invoices) - SUM the money column here, not COUNT.
QUESTION 22 · EXCEPT
Exercise: Print the list of products not sold in 2007 but sold in 2006.
(SELECT sp.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
WHERE YEAR(NGHD) = 2006)
EXCEPT
(SELECT sp.MASP, TENSP
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
WHERE YEAR(NGHD) = 2007);EXCEPT takes rows present in the first set but not present in the second set.
"Present in 2006 but not in 2007" is exactly a set-subtraction operation - EXCEPT expresses this directly, more so than writing it with NOT IN.
QUESTION 23 · HAVING
Exercise: List the products (MASP, TENSP) sold by at least 2 different employees.
SELECT sp.MASP, TENSP, COUNT(DISTINCT MANV) AS SLNVBan
FROM SANPHAM sp JOIN CTHD ct ON sp.MASP = ct.MASP
JOIN HOADON hd ON hd.SOHD = ct.SOHD
GROUP BY sp.MASP, TENSP
HAVING COUNT(DISTINCT MANV) >= 2;HAVING filters on a result that's already been GROUP BY'd (unlike WHERE, which filters before grouping).
The condition "at least 2 different employees" can only be evaluated after counting DISTINCT MANV per product - so it must be filtered with HAVING, not WHERE.
QUESTION 24 · EXCEPT
Exercise: Print the list of customers who did not purchase any product manufactured in Thailand.
(SELECT MAKH, HOTEN
FROM KHACHHANG)
EXCEPT
(SELECT DISTINCT kh.MAKH, HOTEN
FROM KHACHHANG kh JOIN HOADON hd ON kh.MAKH = hd.MAKH
JOIN CTHD ct ON hd.SOHD = ct.SOHD
JOIN SANPHAM sp ON sp.MASP = ct.MASP
WHERE NUOCSX = 'Thai Lan');EXCEPT takes the rows in the first set (every customer) that don't appear in the second set (customers who have purchased at least 1 Thai product, found through the chain JOIN KHACHHANG ➜ HOADON ➜ CTHD ➜ SANPHAM).
"Did not purchase any Thai product" is equivalent to "every customer minus those who purchased at least 1 Thai product" - this is exactly a set-subtraction operation, so EXCEPT is the most natural tool for this "none of..." phrasing.
Alternative approach (NOT EXISTS)
SELECT kh.MAKH, HOTEN
FROM KHACHHANG kh
WHERE NOT EXISTS
(
SELECT *
FROM HOADON hd JOIN CTHD ct ON hd.SOHD = ct.SOHD
JOIN SANPHAM sp ON sp.MASP = ct.MASP
WHERE hd.MAKH = kh.MAKH
AND sp.NUOCSX = 'Thai Lan'
);QUESTION 25 · TOP WITH TIES
Exercise: Find the invoice with the highest total value in 2006, printing (SOHD, NGHD, TRIGIA).
SELECT TOP 1 WITH TIES SOHD, NGHD, TRIGIA
FROM HOADON
WHERE YEAR(NGHD) = 2006
ORDER BY TRIGIA DESC;Sorts the 2006 invoices by TRIGIA descending, then takes TOP 1; WITH TIES also keeps every other invoice that reaches the exact same highest value, not just 1 row.
With plain TOP 1 (no WITH TIES), if 2 invoices tie for the highest value, SQL Server would only return 1 of them at random - the other would be missed. WITH TIES guarantees no data is lost in a tie.
Alternative approach (MAX subquery)
SELECT SOHD, NGHD, TRIGIA
FROM HOADON
WHERE YEAR(NGHD) = 2006
AND TRIGIA = (SELECT MAX(TRIGIA)
FROM HOADON
WHERE YEAR(NGHD) = 2006);