# Lab02 操作手册 ## 1. 实验目标 本实验完成以下内容: - `StudentCourse` 数据库上的简单查询 - `StudentCourse` 数据库上的关系查询与连接 - `StudentCourse` 中更新、删除、触发器与存储过程 - `SPJ` 数据库上的查询与 DML 操作 - 一个嵌入式 SQL 访问数据库的示例程序 ## 2. 截图原则 考虑到 DataGrip 中一个结果 `tab` 通常只对应一次查询,本次 Lab02 改为: - 每一类有代表性的查询结果单独截图 - 每一次关键的增、删、改操作结果单独截图 - 触发器、存储过程、嵌入式 SQL 各自保留独立截图 这样虽然截图数量比精简版多一些,但逻辑会更完整,也更符合“每次增删改查都留证”的实验习惯。 ## 3. 前置条件 - 已完成 Lab01,并已成功创建 `StudentCourse` 和 `SPJ` - Docker 容器 `db-lab-mysql8` 已启动 如容器未启动,在项目根目录执行: ```bash docker compose up -d docker compose ps ``` 本步骤不需要截图。 ## 4. 执行方式说明 建议按 [Lab02/sql/lab2.sql](../sql/lab2.sql) 的大节分段执行: - 第 1 节:恢复 `StudentCourse` 基础数据 - 第 2 节:简单查询 - 第 3 节:关系查询与连接 - 第 4 节:DML 操作 - 第 5 节:触发器与存储过程 - 第 6 节:恢复 `SPJ` 基础数据 - 第 7 节:`SPJ` 查询 - 第 8 节:`SPJ` 的 DML 操作 - 第 9 节:运行嵌入式 SQL 示例程序 其中第 1 节和第 6 节主要用于恢复初始状态,一般不需要单独截图。 ## 5. StudentCourse 简单查询 执行脚本中的第 2 节,并分别保留下列结果。 执行: ```sql USE StudentCourse; SELECT * FROM student ORDER BY s_no; SELECT s_no, s_name, s_major FROM student WHERE s_major = '信息管理与信息系统'; SELECT s_no, s_name, s_birthdate FROM student WHERE s_sex = '女' AND s_birthdate > '2005-01-01'; SELECT s_name, s_birthdate FROM student ORDER BY s_birthdate DESC; SELECT s_name, s_birthdate FROM student ORDER BY s_birthdate LIMIT 2; SELECT s_major, COUNT(*) AS student_count FROM student GROUP BY s_major; ``` 请分别截图: - `shot01.png`:全表查询结果 - `shot02.png`:按专业条件查询结果 - `shot03.png`:多条件查询结果 - `shot04.png`:排序查询结果 - `shot05.png`:`LIMIT` 查询结果 - `shot06.png`:分组统计结果 ## 6. StudentCourse 关系查询与连接 执行脚本中的第 3 节,并分别保留下列结果。 执行: ```sql SELECT s.s_name, c.c_name, sc.grade FROM student s JOIN sc ON s.s_no = sc.s_no JOIN course c ON sc.c_no = c.c_no; SELECT s.s_name, c.c_name, sc.grade FROM student s JOIN sc ON s.s_no = sc.s_no JOIN course c ON sc.c_no = c.c_no WHERE sc.grade > (SELECT AVG(grade) FROM sc WHERE grade IS NOT NULL); SELECT c.c_name, MAX(sc.grade) AS max_grade, MIN(sc.grade) AS min_grade, AVG(sc.grade) AS avg_grade, COUNT(sc.s_no) AS student_count FROM course c LEFT JOIN sc ON c.c_no = sc.c_no GROUP BY c.c_no, c.c_name; SELECT s.s_no, s.s_name, s.s_major, AVG(sc.grade) AS avg_grade, COUNT(sc.c_no) AS course_count FROM student s LEFT JOIN sc ON s.s_no = sc.s_no GROUP BY s.s_no, s.s_name, s.s_major ORDER BY avg_grade DESC; SELECT s.s_name, c.c_name, sc.grade, CASE WHEN sc.grade >= 90 THEN 'A' WHEN sc.grade >= 80 THEN 'B' WHEN sc.grade >= 70 THEN 'C' WHEN sc.grade >= 60 THEN 'D' ELSE 'F' END AS grade_level FROM student s JOIN sc ON s.s_no = sc.s_no JOIN course c ON sc.c_no = c.c_no; ``` 请分别截图: - `shot07.png`:三表连接查询结果 - `shot08.png`:高于平均分的成绩查询结果 - `shot09.png`:按课程统计结果 - `shot10.png`:按学生统计结果 - `shot11.png`:成绩等级划分结果 ## 7. StudentCourse 的更新与删除 执行脚本中的第 4 节,并在每次更新或删除之后保留对应结果。 执行: ```sql SELECT s_no, s_name, s_major FROM student WHERE s_no = '20240003'; SELECT s_no, s_name, s_email FROM student WHERE s_no = '20240001'; SELECT s_no, c_no, grade FROM sc WHERE c_no = 'C0001'; SELECT * FROM student ORDER BY s_no; ``` 请分别截图: - `shot12.png`:专业更新结果 - `shot13.png`:邮箱更新结果 - `shot14.png`:成绩更新结果 - `shot15.png`:删除无选课记录学生后的结果 ## 8. 触发器与存储过程 执行脚本中的第 5 节,并分别保留以下结果。 执行: ```sql CALL proc_query_student_courses('20240001'); CALL proc_raise_course_grade('C0004', 3); SELECT * FROM grade_change_log ORDER BY log_id; ``` 请分别截图: - `shot16.png`:`proc_query_student_courses` 调用结果 - `shot17.png`:`proc_raise_course_grade` 调用结果 - `shot18.png`:`grade_change_log` 日志结果 ## 9. SPJ 查询 执行脚本中的第 7 节,并分别保留下列结果。 执行: ```sql USE SPJ; SELECT SNAME AS 供应商姓名, CITY AS 所在城市 FROM S; SELECT PNAME AS 零件名称, COLOR AS 颜色, WEIGHT AS 重量 FROM P; SELECT DISTINCT JNO AS 工程代码 FROM SPJ WHERE SNO = 'S1'; SELECT P.PNAME AS 零件名称, SUM(SPJ.QTY) AS 总数量 FROM SPJ JOIN P ON SPJ.PNO = P.PNO WHERE SPJ.JNO = 'J2' GROUP BY SPJ.PNO, P.PNAME; SELECT DISTINCT J.JNAME AS 工程名称 FROM SPJ JOIN S ON SPJ.SNO = S.SNO JOIN J ON SPJ.JNO = J.JNO WHERE S.CITY = '上海'; SELECT DISTINCT PNO AS 零件代码 FROM SPJ WHERE SNO IN (SELECT SNO FROM S WHERE CITY = '上海'); SELECT JNO AS 工程代码 FROM J WHERE JNO NOT IN ( SELECT DISTINCT SPJ.JNO FROM SPJ JOIN S ON SPJ.SNO = S.SNO WHERE S.CITY = '天津' ); ``` 请分别截图: - `shot19.png`:供应商与城市查询结果 - `shot20.png`:零件名称、颜色和重量查询结果 - `shot21.png`:供应商 `S1` 参与的工程代码 - `shot22.png`:工程 `J2` 的零件数量统计 - `shot23.png`:上海供应商参与的工程名称 - `shot24.png`:上海供应商供应的零件代码 - `shot25.png`:不使用天津供应商零件的工程代码 ## 10. SPJ 的 DML 操作 执行脚本中的第 8 节,并在每次关键修改后保留结果。 执行: ```sql SELECT * FROM P ORDER BY PNO; SELECT * FROM SPJ WHERE PNO = 'P6' AND JNO = 'J2'; SELECT * FROM S ORDER BY SNO; SELECT * FROM SPJ WHERE SNO = 'S2'; ``` 请分别截图: - `shot26.png`:红色零件更新为蓝色后的结果 - `shot27.png`:`(S5, P6, J2)` 改成 `(S3, P6, J2)` 后的结果 - `shot28.png`:删除供应商 `S2` 后的结果 - `shot29.png`:恢复 `S2` 并新增供应记录后的结果 ## 11. 运行嵌入式 SQL 示例程序 示例程序文件为: [embedded_sql_demo.py](embedded_sql_demo.py) 若本机尚未安装 `PyMySQL`,先执行: ```bash python3 -m pip install pymysql ``` 然后在项目根目录执行: ```bash python3 Lab02/manual/embedded_sql_demo.py ``` 程序会分别查询: - `StudentCourse` 中指定学生的选课成绩 - `SPJ` 中指定城市的供应商信息 请截图并命名为 `shot30.png`。 ## 12. 建议截图总数 本次 Lab02 建议保留 30 张截图。 这些截图虽然比精简版多,但更完整覆盖了: - 简单查询 - 关系查询与连接 - 更新与删除 - 触发器与存储过程 - `SPJ` 查询 - `SPJ` 的 DML - 嵌入式 SQL 如果后续写报告时觉得版面太挤,可以从这 30 张中再挑选最有代表性的结果放入正文,其余作为备选。 ## 13. 完成后的存放位置 请将最终保留的截图放入: `Lab02/report/assets/shots/` 截图完成后,告诉我“Lab2 已执行完成,开始写报告”。