ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

悉尼大学数据库课程实战:从ER建模到SQL优化全攻略

2026/9/26 22:12:36 拓冰建站 浏览量
悉尼大学数据库课程实战:从ER建模到SQL优化全攻略 简介悉尼大学 Database Management SystemCOMP9120课程资料包系统整理数据库管理系统核心知识点适合高校学生、数据库初学者及备考复习者使用。资料围绕数据模型、关系代数与SQL、事务处理与ACID、并发控制、数据库设计及安全性等主题展开覆盖概念建模、关系模型、高级SQL、完整性约束、规范化、存储索引、查询处理等完整教学链路并配有W1至W13各周教程、课堂讲义与参考答案。压缩包共65个文件以39份PDF讲义与教程为主另含7个SQL脚本、2个PPT课件、4个zip作业包等整体大小17.04MB按周模块整理便于按主题查阅。已有191人学习内含课堂讲义、配套tutorial及参考答案、复习笔记comp9120 review和可运行的SQL脚本能帮助读者理解数据库设计全过程并积累实战经验。1. 悉尼大学 Database Management System 课程这份资源到底能换回什么很多人拿到名校课程资源第一反应是收藏第二反应是等有空了再看然后就没有然后了。悉尼大学的《Database Management System》课程资源是典型的“全而杂”型——讲义、作业原文、参考 SQL 脚本、往届真题、评分标准都有但你如果不知道里面每样东西的价值很容易在“从哪个文件开始”这一步就卡住。这门课的核心不是背概念而是让你在 ER 模型、关系模式、SQL 查询、范式设计、事务隔离级别之间来回切换作业出题风格贴近真实工程项目不是那种抄一遍教材就能过的类型。适合正在修这门课的学生、想系统补数据库基础的开发者、准备数据库方向面试的候选人。我用两条主线把它消化掉一条是讲义和作业的对应关系另一条是把作业脚本跑通之后再用真题和自测题验证自己是不是真会了。2. 把讲义拆成抓分点ER 建模、SQL 与范式的关系在哪里拿到课程资源别急着刷 PPT。悉尼大学这版讲义的结构有明确指向前半部分是 ER 模型和关系模式转换后半部分是 SQL 深度查询与事务处理两块在作业里是连环扣的。你画不好 ER 图后面的建表语句就跟着漏字段SQL 语法不熟作业里那几个大分题就拿不稳。下面按三个核心知识点拆开讲每个都直接对应资源里的作业和真题。2.1 ER 模型到关系模式不是把所有名词都建一张表ER 模型是整门课的起点讲义里也花了大量篇幅讲实体、属性、联系三要素。作业第一题通常是给出一个业务描述让你画 ER 图再转成关系模式。这里最常见的翻车点是“过度建模”——把业务描述里每个名词都当成实体结果画出十几张实体评分标准里反而扣分。判断一个东西该不该单独成实体看它有没有独立属性地址如果只是一行字段那它就是属性如果地址还要管邮编、城市、街道明细那才拆实体。转换规则在资源里有一张对照表我整理成更直观的格式联系类型转换规则示例1:1把任一方的主键并入另一方作为外键员工与工位工位表加 emp_id1:N把“一”方的主键并入“多”方客户与订单订单表加 customer_idM:N新建关联表放双方主键再加联系属性学生与课程选课表放学号和课程号作业里给的主题一般是一家旅行社的业务描述涉及组团和报名属于典型的 M:N 结构。我当时做这一步时养成了一个习惯先用三行文字把实体和联系列出来再画图画完检查一遍“每个联系是不是都变成了关系模式里的外键或关联表”漏一个就是一道大题的分。2.2 SQL 综合查询JOIN、子查询和聚合不是三个孤立章节讲义把 SQL 语法拆成很多小节但作业题是综合的——一张查询题往往同时考 JOIN、GROUP BY、子查询、聚合函数。看起来难实际上有固定套路先确定要哪些表的字段再确定行过滤条件和分组过滤条件的层级。下面这道题是这类作业的典型找出报名了三条及以上线路的客户并统计累计消费SELECT c.customer_id, c.name, COUNT(DISTINCT b.trip_id) AS trip_count, COALESCE(SUM(p.amount), 0) AS total_spent FROM customer c LEFT JOIN booking b ON c.customer_id b.customer_id LEFT JOIN payment p ON b.booking_id p.booking_id GROUP BY c.customer_id, c.name HAVING COUNT(DISTINCT b.trip_id) 3 ORDER BY total_spent DESC;几个关键点说清楚LEFT JOIN 是为了保留没有下过单的客户如果这里用 INNER JOIN那些没有订单记录的客户会直接消失统计结果就偏了COUNT(DISTINCT b.trip_id) 是为了防重复因为一个客户可能在一条线路上下过多次单不去重会把线路数数大COALESCE 把没有支付记录的 SUM 结果从 NULL 兜底成 0不然显示出来的 total_spent 是空白而不是 0HAVING 是分组后的过滤不能写成 WHERE这是作业里批改老师最爱标注的地方。把这四层逻辑捋顺SQL 部分的核心分就到手了。2.3 范式判断与事务隔离级别考试的稳定送分题范式题在资源里的真题中几乎年年出现。讲义给的分解步骤我直接压缩成三步第一步找候选键第二步找部分依赖第三步找传递依赖。这里面最大的坑是有人跳过第一步直接拆拆到一半发现主键都判断错了白做。判断范式时先问“候选键是什么”再看所有非主属性对候选键的依赖关系。比如一张学生表包含学号、姓名、学院、学院地址学号是候选键学院地址依赖学院而不直接依赖学号这就是传递依赖至少要到 2NF 之后才有机会消除。事务部分喜欢考隔离级别的异常组合我把对比表整理出来| 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | 读未提交 READ UNCOMMITTED | 可能 | 可能 | 可能 | | 读已提交 READ COMMITTED | 避免 | 可能 | 可能 | | 可重复读 REPEATABLE READ | 避免 | 避免 | 可能 | | 可串行化 SERIALIZABLE | 避免 | 避免 | 避免 |注意 MySQL 默认是可重复读但它在特定情况下对幻读有一定的处理机制这跟讲义里的标准 SQL 定义有区别考试时以讲义为准作业里以实际数据库行为为准两者不冲突但很多人在简答题里栽在这。3. 跑通课程作业环境准备、DDL 设计到查询优化的完整流程资源到手后最踏实的动作是复现。课程作业提供了 SQL 脚本和参考资料但直接从脚本开始跑是典型的错误顺序。我的流程是三步先搭环境再按作业需求重写 DDL最后用 EXPLAIN 验证作业里那些“大分题”的查询性能。每一步都有参数要关注下面展开讲。3.1 环境准备用 Docker 把 MySQL 8 拉起来课程作业没有限定只能用一种数据库但从讲义里的语法和作业评分标准来看MySQL 8 是最稳的选择。我习惯在 Docker 里跑数据库原因很实在宿主机动过什么乱七八糟的包都不会污染数据库实例不想用了直接删容器重新创建一个就是全新环境。起一个容器只需要一条命令docker run --name dbms_course \ -e MYSQL_ROOT_PASSWORDyour_password \ -e MYSQL_DATABASEtrip_agency \ -p 3306:3306 \ -d mysql:8参数的逻辑--name 是容器名方便后面 docker start/stop 管理MYSQL_ROOT_PASSWORD 是 root 账户密码作业环境不用搞太复杂MYSQL_DATABASE 会在容器首次启动时自动建好一个空库省了一条命令-p 3306:3306 把宿主机的 3306 端口映射到容器的 3306这样本地的 Workbench、命令行工具都能连上。还有一点容易被忽略mysql:8 这个镜像名如果没加 tag拉下来就是当前的最新 8.x 版本。我建议不要用 latest 之外的 tag除非你明确知道作业要求的版本特性。3.2 建库建表把作业要求落成带约束的 DDL作业里的建表要求不会直接给你 SQL它只给业务规则比如“每条线路必须有出发日期”“报名人数必须大于 0”。把这些业务规则翻译成约束是评分标准里的硬性加分项。旅行社团组的典型需求可以浓缩成三张表建表脚本如下CREATE DATABASE IF NOT EXISTS trip_agency CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE trip_agency; CREATE TABLE trip ( trip_id INT PRIMARY KEY AUTO_INCREMENT, destination VARCHAR(100) NOT NULL, start_date DATE NOT NULL, duration_days INT NOT NULL CHECK (duration_days BETWEEN 1 AND 30), base_price DECIMAL(10, 2) NOT NULL DEFAULT 0.00, UNIQUE KEY uk_trip_destination_start (destination, start_date) ); CREATE TABLE booking ( booking_id INT PRIMARY KEY AUTO_INCREMENT, customer_name VARCHAR(80) NOT NULL, trip_id INT NOT NULL, seats INT NOT NULL CHECK (seats 0), booking_date DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_booking_trip FOREIGN KEY (trip_id) REFERENCES trip(trip_id) );字段和约束的每个选择都有讲究。destination VARCHAR(100) 给足余量但不放纵100 个字符足够装绝大多数城市名duration_days 用 CHECK 约束把业务规则“行程长度必须在 1 到 30 天之间”写死在数据库里比在应用层判断靠谱——任何人往库里插数据都逃不过这层检查包括你自己UNIQUE KEY 建在 destination 和 start_date 上实现“同一条线路同一天只能有一条记录”的业务规则这个联合唯一索引也能直接服务后续按日期和目的地的查询booking_date 用 DATETIME 加 DEFAULT CURRENT_TIMESTAMP把“下单时间”的赋值交给数据库而不是靠客户端代码传一个可能差 8 个小时的时间进来。外键约束必须有但要注意 MySQL 里外键列的数据类型必须和父表主键完全一致trip_id 是 INTbooking 表里的 trip_id 也必须是 INT不能是 BIGINT 或 SMALLINT类型不一致建表直接报错。3.3 查询优化让慢查询自己说出慢在哪作业里有几道题数据量故意给得很大直接用最简单的写法能跑通但跑得极慢评分标准里性能是占分的。优化第一步不是猜是用 EXPLAIN 看执行计划。假设作业里有这样一道查询找出所有报名了“蓝色山脉”线路且人数超过两人的客户名单。慢速写法可能是直接在 booking 表上全表扫再用函数处理字段。检查方式就一条命令EXPLAIN SELECT b.customer_name, t.destination, b.seats FROM booking b JOIN trip t ON b.trip_id t.trip_id WHERE t.destination Blue Mountains AND b.seats 2;执行计划里最重要的三列是 type、key、rows。type 显示 ALL 就是全表扫描说明这条查询会一行行翻完整个表数据量上万之后必然慢。加上 JOIN 之后如果 key 列是 NULL说明连接没有走索引代价会更大。实践里我碰到过最典型的情况where 条件里的 t.destination 建了索引但 b.seats 2 没有索引MySQL 优化器在两个条件里只能选一个用最终选了全表扫。解决办法很简单给 booking 表的 seats 字段加一个普通索引或者调整查询条件把选择性更高的列放前面。血泪经验是index 不是加得越多越好一个表三到四个有效索引就够加多了写入变慢加错了优化器也不会认。4. 避坑记录加载课程资源时最常见的五个卡点复现课程资源的过程中环境、脚本、数据都可能出问题。下面五个卡点是我和身边同事实际踩过的每一条都按现象、原因、解决写清楚能帮你省下至少一下午的排查时间。4.1 连接报错Authentication plugin caching_sha2_password cannot be loaded现象用旧版 Navicat 或老版本 Workbench 连接 MySQL 8 容器时报错提示无法加载认证插件连接被拒绝。原因MySQL 8 把默认认证插件换成了 caching_sha2_password而 2020 年之前的客户端工具不认识这个新插件两边握手失败。解决要么升级数据库连接工具到支持 MySQL 8 的版本要么在容器里执行 ALTER USER root% IDENTIFIED WITH mysql_native_password BY your_password; 把认证方式降级。我一般直接升级连接工具因为 mysql_native_password 已经是废弃方向作业做完之后再切回默认认证方式又是一道工序。4.2 中文乱码导入脚本后所有中文注释和字符串全变问号现象SQL 文件里写好的中文目的地名称导入后全部变成 ??查询结果也没法看。原因字符集三级不一致——库的字符集、表的字符集、客户端连接的字符集任何一级不是 utf8mb4中文就会在转换过程中丢失。解决建库时显式指定 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci客户端连接后在执行任何 SQL 之前先 SET NAMES utf8mb4;。如果文件已经导坏了别想着修复数据直接删库重建用正确字符集再导一次更快。4.3 外键约束导致删除失败Cannot delete or update a parent row现象按作业要求清理测试数据时DELETE 一条线路记录报错提示外键约束失败。原因booking 表里还有引用这条线路的报名记录而你建表时没有声明 ON DELETE 策略MySQL 默认拒绝删除被引用的父行。解决课程作业里最常见的预期行为是“删除线路时连带删除报名记录”在 booking 表的外键约束上补 ON DELETE CASCADE或者手动先删子表再删父表。注意如果表已经建好要先用 ALTER TABLE booking DROP FOREIGN KEY fk_booking_trip 把旧约束删掉再加新约束。4.4 同一份 SQL 在不同电脑上结果不一样现象同学之间互相跑同一份作业脚本A 机器上跑出 12 行结果B 机器上只有 9 行两边都说没改过代码。原因两个人后端数据库不一样一个 MySQL 一个 PostgreSQL方言差异导致同一个查询语义变了。比如字符串拼接MySQL 用 CONCATPostgreSQL 用 ||COUNT 在 MySQL 里对 NULL 的处理也和标准 SQL 有差别。解决先确认作业评分的后端是哪个课程一般会在评分标准里写明。如果你本机装的是 PostgreSQL但作业按 MySQL 批改跑出来的边界行为就可能不一致。拿不准的时候统一用 Docker 起课程指定的数据库版本这是最省事的路。4.5 视图查询结果没按 ORDER BY 排序现象作业里要求建一个视图视图内写了 ORDER BY外层查询再去查这个视图时结果顺序是乱的。原因MySQL 对视图内的 ORDER BY 基本是忽略的视图的排序稳定性远比想象中差只有最外层查询的 ORDER BY 才被 MySQL 优化器认真对待。解决视图内不写排序排序全部放到最终查询的外层。如果你建视图的目的是让下游查询拿到有序数据这个诉求本身就不合理视图是逻辑表不是有序结果集。5. 从课程到实战图书馆数据库的迁移练习与考点自查课程作业只是练习真正的考验是能不能把这套方法论带进自己的项目里。我通常建议做完课程内容后做一次迁移练习——挑一个身边最常见的业务用同样的流程从零走一遍。图书馆借阅系统是我常用来做迁移的场景因为它小但五脏俱全能覆盖实体设计、外键关系、NULL 语义三个核心考点。5.1 用课程方法设计一个图书馆借阅系统业务规则读者可以借多本书一本书同一时间只能被一个读者借走借阅记录要保存借出时间和归还时间归还时间为 NULL 表示还没还。三张表就够CREATE TABLE reader ( reader_id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, card_number VARCHAR(20) UNIQUE NOT NULL ); CREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, author VARCHAR(50), isbn VARCHAR(20) UNIQUE ); CREATE TABLE borrow ( borrow_id INT PRIMARY KEY AUTO_INCREMENT, reader_id INT NOT NULL, book_id INT NOT NULL, borrow_date DATE NOT NULL, return_date DATE, CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader(reader_id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(book_id), CHECK (return_date IS NULL OR return_date borrow_date) );这里最值得品的是 borrow 表的设计return_date 用可空字段表达“未归还”这是课程讲义里 NULL 语义的直接应用——NULL 表示“值未知”比用 1900-01-01 这类魔法日期高级得多。业务规则里“一本书同一时间只能被一个读者借走”没有直接用约束写死而是通过应用层配合查询来保证这是作业和真实项目的一个区别学校作业会把规则都塞进 DDL真实项目里部分规则必须在事务里控制因为光靠数据库约束表达不了“同一时刻”这种时间窗口条件。这条分界线很重要能想明白就说明你理解这门课为什么既讲 DDL 又讲事务。5.2 课程真题的高频考点自查清单迁移练习做完我建议再用资源里真题的高频考点自查一遍每一条对应一个能力点给定一组函数依赖判断该关系达到第几范式把一条包含 JOIN、子查询、GROUP BY 的查询写成等价形式说出可重复读隔离级别下幻读为什么会出现为一个慢查询设计索引并用 EXPLAIN 验证 type 字段从 ALL 变成 range 或 ref解释视图和物化视图在性能上的本质区别。这五条如果能不看讲义各写一篇小短文说明知识已经内化了而不是临时记住的。6. 验证作业结果的方法从结果集对账到 EXPLAIN 确认索引真实生效作业写得对不对不靠自我感觉靠验证。课程资源里给了参考答案但参考答案也是人写的照抄没意义对答案才是有效动作。我给自己定的验证流程是两层第一层验证功能第二层验证性能两层都过了才算这道题真正做完。功能验证的核心是“结果集对账”。先看行数是否一致再看关键字段值是否一致最后看 NULL 和边界值。方法很简单把参考答案的查询和你自己的查询分别导出结果用一条 SQL 做差集对比-- 找出自己查询里有、但参考答案里没有的记录 SELECT * FROM your_result WHERE NOT EXISTS (SELECT 1 FROM reference_result r WHERE r.id your_result.id);这个技巧是从排查同步数据不一致的实战里带出来的课程作业同样能用。注意如果两边行数一致但差集对比有结果说明主键选错了或者有重复数据这种隐蔽错误比直接报错更危险。性能验证则要落到 EXPLAIN 的具体字段上。我的检查顺序是第一看 type从 ALL 变成 range 或 ref 说明索引生效了第二看 key确认 MySQL 实际用的是你创建的那个索引第三看 rows估算扫描行数有没有明显下降。如果 type 还是 ALL说明优化器的选择和你预期的不一致这时候不要硬加索引先看看是不是 WHERE 条件里对索引列做了函数运算比如 WHERE DATE(start_date) 2024-01-01 会让索引失效改成 start_date 2024-01-01 AND start_date 2024-01-02 才能走到索引。性能验证还有个容易被忽略的细节索引命中和数据量强相关。作业数据量小时全表扫描可能比走索引还快EXPLAIN 里优化器选了 ALL 不代表你的索引设计错了。我在排查的时候会把数据量灌到真实规模再下结论而不是在小数据集上急着优化。从那以后我每次拿到课程资源都强制自己先跑通环境、再完整导入脚本、最后才对答案改查询三道工序缺一道都不敢说自己会用。课程资源里的作业和真题是难得的高质量输入照着这套流程复现一遍比随便翻翻讲义有用得多。希望帮到你。本文还有配套的精品资源点击获取