All Revision Notes

COM6002 Big Data Management · Week 4

SQL

COM6002 · Week 4 · Source: 4-SQL.pptx (70 slides)

Contents · 16 sections

🎯 本講重點 (Learning outcomes)

  • 知道 SQL 的四類語言 DDL / DML / DQL / DCL;SQLite 的優缺點
  • 識寫 CREATE TABLE、INSERT、DELETE、DROP、CREATE INDEX、UPDATE;理解 data types 同 NULL
  • 識寫 query:SELECT / DISTINCT / WHERE / ORDER BY / LIMIT / OFFSET / CASE
  • 掌握各種 JOIN(inner、left / right outer、Cartesian、self join、multiple tables)同 alias
  • 掌握 aggregate functions、GROUP BY、HAVING,以及 WHERE vs HAVING、execution order
  • 識用 set operations(UNION / UNION ALL / INTERSECT / EXCEPT)、sub-query 同 WITH

⭐ 考試必溫 Top-5

  1. JOIN 類型:INNER vs LEFT vs RIGHT vs Cartesian;Cartesian + WHERE = inner join;self join 例子(比 101 更好成績)
  2. Aggregate functions 忽略 NULL;GROUP BY 要用 unique key(StudentID 而唔係 StudentName)
  3. WHERE vs HAVING(group by 之前 vs 之後;原始 attribute vs aggregated data)
  4. Execution order:FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET
  5. Sub-query / WITH:「平均每個 major 修幾多科」兩步法(count 唔可以同一句再 average)

0. 點樣跑本頁所有例子(Runnable setup)

Slide 2 建議用 db-fiddle.com(online playground)或者 VS Code + SQLite(Python 內置 sqlite3 package;可裝 SQLite Viewer extension 睇內容);練習可用 LeetCode database problems。以下 script 建立本頁用到的所有 tables(跟 slides 的數據)。貼入 db-fiddle(揀 SQLite)或者用下面 Python 跑:

-- setup.sql
CREATE TABLE Student (            -- slide 8–23
    Student_ID CHAR(4),
    Name       VARCHAR(100),
    Year       INT
);
INSERT INTO Student VALUES ('s123', 'Amy', 1);
INSERT INTO Student VALUES ('s234', 'Bob', 2);
INSERT INTO Student VALUES ('s345', 'Cathy', 1);

-- slide 24–29、35–36、57–63(為免同上面撞名,Student 改叫 Stud)
CREATE TABLE Stud   (StudentID INT, StudName VARCHAR(50), Major VARCHAR(10));
INSERT INTO Stud VALUES (1,'Amy','DS'), (3,'Bob','CS'), (6,'Cathy','CS');
CREATE TABLE Result (StudentID INT, ModuleCode CHAR(6), Grade CHAR(1));
INSERT INTO Result VALUES (1,'CS1002','B'), (1,'CS1001','A'), (3,'CS1001','A'), (6,'CS1001','B');
CREATE TABLE Module (ModuleCode CHAR(6), ModuleName VARCHAR(100));
INSERT INTO Module VALUES ('CS1001','Programming'), ('CS1002','Databases');  -- module 名為示範自訂

-- slide 30–42(Student AS a / Result AS b)
CREATE TABLE S (ID INT, Name VARCHAR(50));
INSERT INTO S VALUES (102,'Mary'), (103,'Alice'), (101,'Alice');
CREATE TABLE R (ID INT, MCode CHAR(6), Grade CHAR(1));
INSERT INTO R VALUES (101,'CS1001','B'), (103,'CS1002','B'), (104,'CS1001','A');

-- slide 45–50(每行係一張 order:Shop ID + Amount)
CREATE TABLE T (Shop INT, Amount INT);
INSERT INTO T VALUES (1,1),(1,2),(1,3),(2,2),(2,4),(3,1),(3,3),(3,4);
# run_sql.py — 用 Python 內置 sqlite3 執行
import sqlite3
con = sqlite3.connect(":memory:")          # 或 "school.db" 存落 file
con.executescript(open("setup.sql").read())

def q(sql):
    cur = con.execute(sql)
    print([d[0] for d in cur.description])  # column names
    for row in cur.fetchall():
        print(row)

q("SELECT * FROM Student")
⚠️ 引號

Slides 用 “Mary” 呢類雙引號表示 text。Standard SQL 字串用 單引號 'Mary'(雙引號喺 standard SQL / PostgreSQL 係 identifier,即欄名)。MySQL 同 SQLite 對雙引號比較寬鬆,但考試 / 實務建議用單引號。本頁 code 一律用單引號。

1. Tools、SQLite 與 SQL 分類

Cloud platforms [Self-study]:Oracle(Always Free Tier,要 credit card)、GCP Cloud SQL、Azure SQL Database、AWS Amazon RDS(Free Tier)。

1.1 SQLite vs standard RDBMS

ProsCons
Lightweight;can be embedded in the application(一個 file 就係一個 DB)Database level locking instead of row-based locking(鎖成個 DB 而唔係個別 data items);Not scalable(single node);Not for remote access;No built-in authentication

1.2 SQL 四類語言

⭐ 考試重點
類別全名例子
DDLData Definition LanguageCreate / delete database and tables(CREATE TABLE, DROP TABLE, CREATE INDEX)
DMLData Manipulation LanguageAdd / remove data(INSERT, UPDATE, DELETE)
DQLData Query LanguageQuery(SELECT)— the focus here
DCLData Control LanguageGrant access(GRANT)

SQL versions:唔同 RDBMS 支援的 syntax 可能有少少唔同。

2. Basic commands & data types

CommandDescription
CREATE TABLE [table] ([attribute1] [type1], [attribute2] [type2], ...);Create new table
INSERT INTO [table] VALUES ([value], …, [value]);Inserting a record
DELETE FROM [table] WHERE [condition]Delete record(s) from tables
DROP TABLE [table];Delete a table

ExamCommands are case-insensitive!(select = SELECT)Username and password are case-sensitive!

2.1 Data types(commonly used)

Data typeDescription
INTInteger
DECIMAL(a, b)Decimal numbers with a digits(總位數)and b decimals(小數位)。例:DECIMAL(5, 2): 347.88;DECIMAL(3, 1): 11.2;DECIMAL(3, 2): 3.14
VARCHAR(n)Text with variable length, at most n characters
CHAR(n)Fixed length text, exactly n characters
BLOBLarge binary data, e.g., image
DATE / TIME'2047-07-01' / '11:22:33'
⚠️ 易錯

DECIMAL(5, 2) 嘅 5 係總位數(包括小數),唔係整數位。所以最大係 999.99;1234.5 放唔落。DECIMAL(3, 2) 最大 9.99。

2.2 Create & insert(slides 8–9)

CREATE TABLE Student (
    Student_ID char(4),
    Name varchar(100),
    Year int
);
INSERT INTO Student VALUES ('s123', 'Amy', 1);
INSERT INTO Student VALUES ('s234', 'Bob', 2);
INSERT INTO Student VALUES ('s345', 'Cathy', 1);

Notes:string data 用引號包住;VALUES 要 follow the order of attributes in the table。

2.3 NULL

ExamNULL 係 special value,兩個 interpretations:(1) The data value does not exist;(2) The data value is not known。Rule of thumb:database design 應避免有(太多)NULL。Empty string ("") ≠ NULL:""表示有值,而且知道個值係 ""。

Student IDEnglish NameChinese name
s123AmyNULL(未知 / 冇)
s234Bob""(知道係空)
s345CathyLi Yi
-- 搵 NULL 要用 IS NULL,唔可以用 = NULL
SELECT * FROM R WHERE Grade IS NULL;

2.4 Creating index

CREATE INDEX idx  ON Student (Name);
CREATE INDEX idx2 ON Student (Name, Year);   -- composite index(同一 DB 內 index 名要唯一)

3. Basic query:SELECT, DISTINCT, WHERE

SELECT * FROM Student;               -- Option 1:* = all attributes
SELECT Name, Year FROM Student;      -- Option 2:逐個列出 attribute

Potential errors:Syntax error(睇 error message);Table / Column doesn't exist(檢查串字)。

3.1 DISTINCT

SELECT DISTINCT Year FROM Student;   -- 1, 2      (去除重複)
SELECT Year FROM Student;            -- 1, 2, 1

3.2 WHERE clause

SELECT * FROM S
WHERE (ID > 100 AND ID < 102) OR Name = 'Mary';

Table S:(101, Alice), (102, Mary), (103, Alice)。Result(lecturer notes):101 Alice(100 < 101 < 102)、102 Mary(Name = Mary)。103 Alice 兩個條件都唔符合。

Parameter format:text 要用引號(Name = 'Mary');integer 唔使(ID > 100)。

Comparison operatorsLogical operators
=, >, <, >=, <=, <>(not equal)AND(both), OR(either), NOT(reverse)
SELECT * FROM S WHERE ID <> 101;            -- 102 Mary, 103 Alice
SELECT * FROM S WHERE NOT Name = 'Alice';    -- 102 Mary

4. ORDER BY, LIMIT, OFFSET

SELECT * FROM Result ORDER BY Grade;                         -- 預設 ascending
SELECT * FROM Result ORDER BY Grade DESC;                    -- 加 DESC 變 descending
SELECT * FROM Result ORDER BY Grade DESC, StudentID DESC;    -- 先按 Grade,同分再按 StudentID;兩個都 DESC
SELECT * FROM Result LIMIT 2;                                -- 只要頭 2 個
SELECT * FROM Result LIMIT 2 OFFSET 2;                       -- 跳過頭 2 個,攞第 3–4 個

Slide 例子:LIMIT 10 OFFSET 10 = return the 11th to 20th records(常用嚟做分頁)。注意 DESC 只作用喺佢前面嗰一個 attribute——slide 20 兩個都要寫 DESC。

Order of commands:SELECT [DISTINCT] … FROM … WHERE … ORDER BY …——ORDER BY 要放喺 WHERE 之後。

⚠️ 留意

Standard SQL / MySQL 冇 LIMIT 唔可以單獨用 OFFSET(slide 22 第一個例子 SELECT * FROM Result OFFSET 10 喺 PostgreSQL 得,喺 MySQL / SQLite 會 error)。保險寫法係 LIMIT n OFFSET m。

5. JOIN

可以同時由多個 tables 拎資料;There is (usually) a join condition! Types of join:Inner Join;Left / Right (OUTER) JOIN;Full outer join;Cartesian Join;Self Join (example);Theta join、Natural join 等等。

5.1 INNER JOIN

SELECT * FROM Stud INNER JOIN Result
ON Stud.StudentID = Result.StudentID;
StudentIDStudNameMajorModuleCodeGrade
1AmyDSCS1002B
1AmyDSCS1001A
3BobCSCS1001A
6CathyCSCS1001B

(實際 SELECT * 會出兩個 StudentID 欄,一個來自每個 table。)多個 tables 可能有同名 attributes,用 [table name].[attribute name] 區分。

5.2 Alias

SELECT * FROM Stud AS s INNER JOIN Result AS r ON s.StudentID = r.StudentID;
SELECT * FROM Stud s INNER JOIN Result r ON s.StudentID = r.StudentID;   -- 唔寫 AS 都得
SELECT s.StudentID AS newname FROM Stud AS s;                             -- 改 attribute 名

5.3 LEFT / RIGHT (OUTER) JOIN

Student (S):102 Mary, 103 Alice, 101 Alice。Result (R):101 CS1001 B, 103 CS1002 B, 104 CS1001 A。

SELECT a.ID, a.Name, b.MCode, b.Grade
FROM S AS a LEFT JOIN R AS b ON a.ID = b.ID;
a.IDNameMCodeGrade
102MaryNULLNULL
103AliceCS1002B
101AliceCS1001B
SELECT a.ID, a.Name, b.MCode, b.Grade
FROM S AS a RIGHT JOIN R AS b ON a.ID = b.ID;
a.IDNameMCodeGrade
NULLNULLCS1001A
103AliceCS1002B
101AliceCS1001B
⭐ 考試重點:Join 結果點估
  • INNER:只保留兩邊都 match 的 rows(101, 103)
  • LEFT:左表全部保留;右表冇 match 就填 NULL(Mary 冇成績)
  • RIGHT:右表全部保留;左表冇 match 就填 NULL(104 唔喺 Student 表)
  • FULL OUTER:兩邊全部保留(102 + 101 + 103 + 104)
-- FULL OUTER JOIN(SQLite ≥ 3.39 / PostgreSQL 支援;MySQL 要用 LEFT UNION RIGHT 模擬)
SELECT a.ID, a.Name, b.MCode, b.Grade FROM S AS a FULL OUTER JOIN R AS b ON a.ID = b.ID;

5.4 Cartesian join

SELECT * FROM S AS a, R AS b;          -- 3 × 3 = 9 rows:每個組合都出

Cartesian join(cross product)唔需要 join condition:∣S∣×∣R∣|S| \times |R| rows。ExamInner join = Cartesian join + WHERE:

SELECT * FROM S AS a, R AS b WHERE a.ID = b.ID;   -- 同 INNER JOIN 結果一樣

(Slides 32–33 的 Result 表第三行係 103 CS1001 A,所以 slide 的結果有 3 行:101/CS1001、103/CS1002、103/CS1001;用本頁 R 表(第三行係 104)就只得 2 行。)

Performance? 先做 Cartesian join 再 WHERE 理論上慢好多,但 existing DBMS 會自動 "re-write" query 去 optimize,所以速度一樣,兩種寫法都可以用。

5.5 Joining multiple tables

SELECT s.StudName, m.ModuleName, r.Grade
FROM Stud s INNER JOIN Result r ON s.StudentID = r.StudentID
            INNER JOIN Module m ON r.ModuleCode = m.ModuleCode;

Just add another join statement in the FROM clause。Each join action is done sequentially:(Student + Result) 先,結果再 + Module。

5.6 Self join example

Query:Who has a better grade on CS1001 than student ID 101?(R 表:101 CS1001 B、103 CS1002 B、104 CS1001 A)

難處:WHERE clause 係逐 row 計算,唔可以跨 rows 比較。Idea:用 同一個 table 兩份 copy(a, b),Cartesian join 將不同 rows 帶入同一個 joined row,就可以比較。(Self join 唔係新的 join 類型,只係示範點解有時同一 SQL 要用同一 table 多次。)

SELECT b.ID
FROM R a, R b
WHERE a.ID = 101 AND a.MCode = 'CS1001'     -- a:搵 101 喺 CS1001 的成績
  AND b.ID <> 101 AND b.MCode = 'CS1001'    -- b:其他學生喺 CS1001 的成績
  AND b.Grade < a.Grade;                    -- 'A' < 'B'(字母序),即 b 成績較好
-- 結果:104

Joined row 係 (a.ID=101, CS1001, B, b.ID=104, CS1001, A),用 a.xxx / b.xxx 引用各自的 attributes。

Another solution:刪走 b.ID <> 101,因為 the condition b.Grade < a.Grade will also filter the student 101 in table b(B < B 為 false)。兩個 solution 結果一樣;第二個較簡潔,第一個較清楚表達意圖。

⚠️ 易錯

用字母比較 grade 只係因為 A < B < C… 剛好等於「愈細愈好」。如果有 "A-"、"B+" 呢類 grade,字母序就唔啱,要另外 map 成 grade point。

6. CASE

SELECT StudentID, ModuleCode, Grade,
    CASE
        WHEN Grade = 'A' THEN 'Excellent'
        WHEN Grade = 'B' THEN 'Good'
        ELSE 'Pass'
    END AS Status
FROM Result;

結果:(1, CS1002, B, Good)、(1, CS1001, A, Excellent)、(3, CS1001, A, Excellent)、(6, CS1001, B, Good)。CASE 由上至下檢查,第一個成立的 WHEN 就用佢的 THEN;全部唔成立就用 ELSE。

7. Aggregate query

For finding statistics:minimum, maximum, sum, average, count;配合 GROUP BY 同 HAVING。Key idea:Notice the intermediate results are also tables!

7.1 Aggregate functions

T 的 B 欄:1, 2, 3, 2, 4, 1, 3, 4(A 欄係 1,1,1,2,2,3,3,3)。

SQLResult
SELECT SUM(B) FROM T20
SELECT COUNT(*) FROM T8
SELECT AVG(B) FROM T2.5
SELECT MIN(B) FROM T1
SELECT MAX(B) FROM T4
-- 用本頁 T(Shop, Amount),數據同 slide 的 (A, B) 一樣
SELECT SUM(Amount), COUNT(*), AVG(Amount), MIN(Amount), MAX(Amount) FROM T;   -- 20, 8, 2.5, 1, 4
⭐ 考試重點:Aggregate functions on NULL values — NULL values are ignored

將 B 欄其中兩個值(原本 2 同 1)改做 NULL:B = 1, 2, 3, NULL, 4, NULL, 3, 4

  • COUNT(B) = 6(唔計 NULL);但 COUNT(*) 仍然 = 8(數 rows)
  • SUM(B) = 1+2+3+4+3+4 = 17
  • MIN(B) = 1
  • 延伸:AVG(B) = 17 / 6 ≈ 2.83(分母係 6 而唔係 8!)
-- 自己試:
CREATE TABLE TN (A INT, B INT);
INSERT INTO TN VALUES (1,1),(1,2),(1,3),(2,NULL),(2,4),(3,NULL),(3,3),(3,4);
SELECT COUNT(B), COUNT(*), SUM(B), MIN(B), AVG(B) FROM TN;   -- 6, 8, 17, 1, 2.833…

7.2 GROUP BY

每個 record 係一張 order(Shop ID, Amount)。

SELECT Shop, SUM(Amount)
FROM T
GROUP BY Shop;
-- Shop 1: 1+2+3 = 6;Shop 2: 2+4 = 6;Shop 3: 1+3+4 = 8

GROUP BY 將 rows 按 Shop 分組(概念上每組係一個細 table),再對每組計 SUM。

7.3 GROUP BY with WHERE

SELECT Shop, SUM(Amount)
FROM T
WHERE Amount > 2
GROUP BY Shop;
-- 先過濾剩 (1,3),(2,4),(3,3),(3,4) → Shop 1: 3;Shop 2: 4;Shop 3: 7

7.4 HAVING

Post-filtering after you get the aggregate results:

SELECT Shop, SUM(Amount) AS Total
FROM T
WHERE Amount > 2
GROUP BY Shop
HAVING Total > 5;          -- 只剩 Shop 3, Total 7
⚠️ 方言差異

HAVING Total > 5(用 SELECT 的 alias)喺 MySQL / SQLite 得;standard SQL / PostgreSQL 要寫 HAVING SUM(Amount) > 5。後者到處都 work。

⭐ 考試重點:WHERE vs HAVING
WHEREHAVING
處理時間Processed before the group by actionProcessed after the group by action
條件用乜Attributes of the original tablesThe aggregated data:count / sum / average
例子WHERE Amount > 2(逐張 order 過濾)HAVING SUM(Amount) > 5(逐間 shop 過濾)

7.5 GROUP BY 要用乜?

想計每個學生修咗幾多科:

-- Option A:正確
SELECT COUNT(*) FROM Result GROUP BY StudentID;
-- Option B:錯誤 —— 兩個同名學生會被放入同一組
SELECT COUNT(*) FROM Result GROUP BY StudentName;

ExamGROUP BY 要用能唯一識別 entity 的 attribute(key)。Slide 30 的 S 表就有兩個 "Alice"(101、103)——如果按 Name group,佢哋會被當成同一人。

8. Putting it all together:Clause order vs Execution order

SELECT A, B, SUM(C) AS Total
FROM T INNER JOIN W ON T.X = W.Y
WHERE B > 10
GROUP BY A, B
HAVING Total > 100
ORDER BY A DESC

注意寫法次序 唔係 執行次序!

⭐ 考試重點:Execution & evaluation order
  1. FROM clause (including JOINs)
  2. WHERE clause
  3. GROUP BY clause
  4. HAVING clause
  5. SELECT projections
  6. DISTINCT
  7. ORDER BY
  8. LIMIT / OFFSET

推論:WHERE 喺 GROUP BY 之前,所以 WHERE 唔可以用 SUM();ORDER BY 喺 SELECT 之後,所以 ORDER BY 可以用 SELECT 定義的 alias。

9. Set operations

UNION、INTERSECT、EXCEPT——係 set operations(會去除重複),not multiset。要求兩邊 compatible data types and same number of columns。

SELECT ID FROM S UNION     SELECT ID FROM R;   -- 101, 102, 103, 104(去重)
SELECT ID FROM S UNION ALL SELECT ID FROM R;   -- 102, 103, 101, 101, 103, 104(multiset,保留重複)
SELECT ID FROM S INTERSECT SELECT ID FROM R;   -- 101, 103
SELECT ID FROM S EXCEPT    SELECT ID FROM R;   -- 102(喺 S 但唔喺 R)

10. Sub-query 與 WITH

Observation:SELECT 的結果係一個 table → 可以放喺 FROM clause:

SELECT * FROM (SELECT * FROM Stud) AS X;   -- sub-query 必須俾佢一個 table name,否則 error

10.1 例子:每個 major 平均修幾多科?

Expected:DS → 2,CS → 1。唔可以用一句 SQL 直接做——You cannot average a count in the same SQL。分兩步:

Step 1:每個學生修幾多科

SELECT S.StudentID, S.Major, COUNT(R.ModuleCode) AS NumModule
FROM Stud AS S LEFT JOIN Result AS R
ON S.StudentID = R.StudentID
GROUP BY S.StudentID, S.Major;
-- (1, DS, 2), (3, CS, 1), (6, CS, 1)

Step 2:將 Step 1 結果當 table T,每個 major 計平均:

SELECT Major, AVG(NumModule) AS Avg_Num FROM T GROUP BY Major;

Combining together(sub-query):

SELECT Major, AVG(NumModule) AS Avg_Num
FROM (
    SELECT S.StudentID, S.Major, COUNT(R.ModuleCode) AS NumModule
    FROM Stud AS S LEFT JOIN Result AS R
    ON S.StudentID = R.StudentID
    GROUP BY S.StudentID, S.Major
) AS T
GROUP BY Major;
-- CS 1.0 ; DS 2.0

WITH statement(CTE,更易讀):

WITH T AS (
    SELECT S.StudentID, S.Major, COUNT(R.ModuleCode) AS NumModule
    FROM Stud AS S
    LEFT JOIN Result AS R ON S.StudentID = R.StudentID
    GROUP BY S.StudentID, S.Major
)
SELECT Major, AVG(NumModule) AS Avg_Num
FROM T
GROUP BY Major;
💡 點解用 LEFT JOIN 同 COUNT(R.ModuleCode)?

如果有學生一科都冇修,INNER JOIN 會令佢消失,平均就會偏高。LEFT JOIN 保留佢(R 的欄位係 NULL),而 COUNT(R.ModuleCode) 忽略 NULL → 計到 0。如果用 COUNT(*) 就會錯計成 1。呢個係 aggregate 忽略 NULL 的實際應用。

11. Updating your data

UPDATE Student SET Year = 2 WHERE Student_ID = 's345';   -- 格式:UPDATE t SET c1 = v1, c2 = v2, ... WHERE condition
DELETE FROM Student WHERE Year = 2;                      -- 刪走 Bob 同 Cathy
⚠️ 危險

Be careful. If there is no WHERE condition, the entire table will be affected. DELETE FROM student → All data in the student table will be removed;UPDATE Student SET Year = 2 → 全部人都變 Year 2。

12. More features & query optimization [Self-study]

DBA 相關 features:Primary key、Foreign keys、Database design [Normal forms]、Constraints、Not Null、Stored procedures、Triggers、Views、Backup and recovery、Access control。

-- 示範:Primary key / Foreign key / NOT NULL 約束
CREATE TABLE Student2 (StudentID INT PRIMARY KEY, Name VARCHAR(100) NOT NULL);
CREATE TABLE Result2 (
    StudentID INT REFERENCES Student2(StudentID),
    ModuleCode CHAR(6),
    Grade CHAR(1),
    PRIMARY KEY (StudentID, ModuleCode)
);

Query plan optimizer:

SELECT * FROM S AS a, R AS b WHERE a.ID = b.ID AND a.Name = 'Tom';

最有效率:先喺 Student 表 filter Name = 'Tom',再計 join——因為 filtered student table 的 records 少好多,join 時要配對的組合大減(filter pushdown)。例:Student 10,000 行、Result 100,000 行,如果 Tom 只有 1 個,先 filter 就只需要用 1 行去 join。

13. In-class exercises & SQL beyond coding

LeetCode basic:Combine Two Tables、Employees Earning More Than Their Managers、Duplicate Emails、Game Play Analysis I、Classes With at Least 5 Students(2 marks,screenshot 提交 Moodle)。More challenging:Delete Duplicate Emails(hint: DELETE query)、Percentage of Users Attended a Contest(hint: ROUND)、Average Time of Process per Machine、The Number of Employees Which Report to Each Employee。

Slide 70:SQL beyond coding——例如喺 Power BI load data from RDBMS、Databricks 的 SQL Editor(截圖,內容擷取不到)。

以下係幾題 basic 題的典型寫法(用題目的 table 名):

-- Combine Two Tables:每個人都要出,冇地址都要 → LEFT JOIN
SELECT p.firstName, p.lastName, a.city, a.state
FROM Person p LEFT JOIN Address a ON p.personId = a.personId;

-- Employees Earning More Than Their Managers:self join
SELECT e.name AS Employee
FROM Employee e JOIN Employee m ON e.managerId = m.id
WHERE e.salary > m.salary;

-- Duplicate Emails:GROUP BY + HAVING
SELECT email AS Email FROM Person GROUP BY email HAVING COUNT(*) > 1;

-- Game Play Analysis I:每個 player 第一次登入日期
SELECT player_id, MIN(event_date) AS first_login FROM Activity GROUP BY player_id;

-- Classes With at Least 5 Students
SELECT class FROM Courses GROUP BY class HAVING COUNT(student) >= 5;

自我測驗 Self-test

Q1. 將以下 SQL 分類為 DDL / DML / DQL / DCL:CREATE INDEX、UPDATE、SELECT、GRANT、DROP TABLE、INSERT。

CREATE INDEX:DDL;UPDATE:DML;SELECT:DQL;GRANT:DCL;DROP TABLE:DDL;INSERT:DML。

Q2. 一個價錢欄最大值係 99999.99,應該用 DECIMAL(?, ?)?DECIMAL(4, 1) 可以存 123.45 嗎?

DECIMAL(7, 2)(共 7 位,其中 2 位小數)。DECIMAL(4,1) 只有 1 位小數、最大 999.9,123.45 會被四捨五入成 123.5(或視乎系統報錯),唔可以準確存。

Q3. 用 S 同 R 表,寫出 S LEFT JOIN R、S RIGHT JOIN R、S INNER JOIN R 的行數,並說明 Cartesian join 有幾多行。

LEFT:3 行(102 Mary 配 NULL、103、101);RIGHT:3 行(103、101、NULL 配 104);INNER:2 行(101、103);Cartesian:3 × 3 = 9 行。

Q4. 寫 SQL:列出喺 CS1001 成績比學生 101 差的學生 ID(用 R 表)。

SELECT b.ID FROM R a, R b WHERE a.ID = 101 AND a.MCode = 'CS1001' AND b.MCode = 'CS1001' AND b.Grade > a.Grade; 用本頁數據結果係空(104 係 A,比 B 好)。同 slide 的 self join 一樣,只係比較方向反轉;b.Grade > a.Grade 已排除 101 自己。

Q5. B = 5, NULL, 3, NULL, 7。COUNT(*)、COUNT(B)、SUM(B)、AVG(B) 各係幾多?

COUNT(*) = 5;COUNT(B) = 3;SUM(B) = 15;AVG(B) = 15 / 3 = 5(NULL 被忽略,分母係 3)。

Q6. 用 T(Shop, Amount) 寫 SQL:只計 Amount ≥ 2 的 orders,列出總額大於 5 的 shops,按總額由大到細排列。結果係乜?

SELECT Shop, SUM(Amount) AS Total FROM T WHERE Amount >= 2 GROUP BY Shop HAVING SUM(Amount) > 5 ORDER BY Total DESC; 過濾後:Shop 1: 2+3 = 5;Shop 2: 2+4 = 6;Shop 3: 3+4 = 7。HAVING > 5 → Shop 3 (7)、Shop 2 (6)。

Q7. 以下 SQL 有咩錯?SELECT Shop, SUM(Amount) FROM T WHERE SUM(Amount) > 5 GROUP BY Shop;

WHERE 喺 GROUP BY 之前執行,嗰陣仲未有 aggregate 結果,所以 WHERE 唔可以用 SUM()。應改為 GROUP BY Shop HAVING SUM(Amount) > 5。

Q8. 寫出 SQL 的 execution order,並解釋點解 ORDER BY 可以用 SELECT 入面的 alias。

FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET。ORDER BY 喺 SELECT 之後執行,alias 已經定義咗,所以用得;WHERE 喺 SELECT 之前,所以(標準上)唔可以用 alias。

Q9. 用 WITH 寫:每個 major 有幾多個學生攞過至少一個 A?(用 Stud / Result)

WITH A AS (SELECT DISTINCT StudentID FROM Result WHERE Grade = 'A') SELECT s.Major, COUNT(*) AS NumA FROM Stud s INNER JOIN A ON s.StudentID = A.StudentID GROUP BY s.Major; 結果:CS 1(Bob)、DS 1(Amy)。DISTINCT 確保同一學生多個 A 只計一次。

Q10. UNION 同 UNION ALL 有咩分別?用 set operations 有咩要求?

UNION 係 set operation,會去除重複;UNION ALL 係 multiset union,保留重複。要求兩邊 same number of columns 同 compatible data types。

一頁總結 Cheat sheet

⭐ 考前速記
  • SQLite:lightweight、embedded;但 DB-level locking、not scalable、no remote access、no auth
  • DDL (CREATE/DROP) / DML (INSERT/UPDATE/DELETE) / DQL (SELECT) / DCL (GRANT);commands case-insensitive,username/password case-sensitive
  • DECIMAL(a,b):a 總位數、b 小數;VARCHAR(n) 可變長;CHAR(n) 固定長
  • NULL = 不存在 / 未知;≠ "";用 IS NULL
  • CREATE INDEX idx ON t (c1, c2)
  • SELECT [DISTINCT] … FROM … WHERE … ORDER BY … [DESC] LIMIT n OFFSET m;<> = not equal
  • INNER = 兩邊 match;LEFT/RIGHT 保留一邊、另一邊 NULL;Cartesian = ∣A∣×∣B∣|A| \times |B|;Cartesian + WHERE = inner join(optimizer 會 rewrite,速度一樣)
  • Alias:Student AS s 或 Student s;multiple joins 逐個順序做
  • Self join:同一 table 兩個 alias,將不同 rows 放入同一 row 比較
  • CASE WHEN … THEN … ELSE … END AS col
  • Aggregates:SUM / COUNT / AVG / MIN / MAX;忽略 NULL(COUNT(*) 除外)
  • WHERE(group 前、原始欄位)vs HAVING(group 後、aggregate)
  • GROUP BY 用 unique key(StudentID 唔係 Name)
  • Execution:FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET
  • UNION / INTERSECT / EXCEPT = set(去重);UNION ALL = multiset
  • Sub-query 要有 table name;WITH T AS (…);average of counts 要兩步
  • UPDATE / DELETE 冇 WHERE = 影響全表
  • Optimizer:先 filter 再 join