大量的更新

This commit is contained in:
zeaslity
2025-11-20 16:18:35 +08:00
parent 776c946772
commit fc72a7312e
30 changed files with 8755 additions and 1327 deletions

View File

@@ -0,0 +1,640 @@
# SQLite综合能力考核大学数据库管理实战
## 介绍:大学数据库场景
为了全面考察您对SQLite的掌握程度我们将围绕一个清晰、易于理解的场景来设计所有练习。这种叙事性的方法不仅能提升学习的趣味性还能帮助您理解SQL命令在实际工作中的应用。
**场景描述:** 您将为一所小型大学设计并管理一个数据库。该数据库需要追踪讲师信息、他们教授的课程、注册课程的学生以及学生的选课和成绩详情。这是一个经典的关系模型非常适合用来测试DDL数据定义语言、DML数据操作语言、DQL数据查询语言和TCL事务控制语言的各项概念。
**实体与关系:**
+ **讲师 (Instructors):** 每位讲师拥有姓名、邮箱和办公室编号。
+ **课程 (Courses):** 每门课程拥有课程名称、所属系别、学分,并由一位讲师授课。
+ **学生 (Students):** 每位学生拥有姓名、邮箱和专业。
+ **选课记录 (Enrollments):** 这是一个连接表,用于关联学生和课程,并记录学生在特定课程中获得的成绩。
这个场景经过精心选择,因为它天然地包含了**一对多关系**(一位讲师可以教授多门课程)和**多对多关系**(一名学生可以选修多门课程,一门课程也可以被多名学生选修),为测试连接查询和参照完整性提供了坚实的基础。
* * *
## 第一部分DDL (数据定义语言) - 构建数据库基础
本部分旨在考察您将业务需求转化为逻辑数据库结构的能力。这不仅考验您的DDL命令语法还涉及数据规范化和数据完整性等设计原则。我们将重点考察 `CREATE TABLE`、数据类型与类型亲和性、`PRIMARY KEY``FOREIGN KEY``NOT NULL``UNIQUE``DEFAULT` 以及 `ALTER TABLE` 等命令。
**题目列表:**
1. 在SQLite中外键约束默认是关闭的这可能导致数据完整性问题 。为了确保后续操作的参照完整性,请写出为当前数据库连接开启外键约束强制执行的命令。  
2. 创建一个名为 `Instructors` 的表,用于存储讲师信息。该表应包含以下列:
+ `instructor_id`: 整数类型,作为主键且能自动增长。
+ `first_name`: 文本类型,不能为空。
+ `last_name`: 文本类型,不能为空。
+ `email`: 文本类型,不能为空,且必须保证其值唯一。
3. 创建一个名为 `Students` 的表,用于存储学生信息。该表应包含以下列:
+ `student_id`: 整数类型,作为主键。
+ `first_name`: 文本类型,不能为空。
+ `last_name`: 文本类型,不能为空。
+ `email`: 文本类型,不能为空,且值唯一。
+ `major`: 文本类型,如果未指定,则默认值为 'Undeclared'。
4. 创建一个名为 `Courses` 的表,用于存储课程信息。该表应包含以下列:
+ `course_id`: 整数类型,作为主键。
+ `title`: 文本类型,不能为空。
+ `department`: 文本类型,不能为空(例如 'CSCI')。
+ `credits`: 整数类型。
+ `instructor_id`: 整数类型,该列应作为外键,引用 `Instructors` 表的 `instructor_id` 列。
5. 修改 `Courses` 表中 `instructor_id` 的外键约束。要求当 `Instructors` 表中的某位讲师记录被删除时,`Courses` 表中由该讲师授课的课程的 `instructor_id` 字段应自动被设置为 `NULL` 
6. 创建一个名为 `Enrollments` 的连接表,用于记录学生选修课程的情况。该表应包含:
+ `student_id`: 整数类型。
+ `course_id`: 整数类型。
+ `grade`: 文本类型,用于存储成绩(如 'A', 'B+', 'C' 等)。
+ 将 `student_id``course_id` 的组合设置为主键(复合主键)。  
假设学校规定,每个学生最多只能选修 5 门课程。仅在数据库层面(不依赖应用程序代码),我们如何实现这个约束?
(提示:查阅 SQLite 关于 CHECK 约束和子查询的文档,思考一个创造性的解决方案。)
7.`Enrollments` 表上定义外键约束:
+ `student_id` 列引用 `Students` 表的 `student_id` 列。
+ `course_id` 列引用 `Courses` 表的 `course_id` 列。
8. 修改 `Enrollments` 表的外键约束。要求当 `Students` 表中的某个学生记录被删除时,该学生所有的选课记录也应自动从 `Enrollments` 表中被删除(级联删除)。  
9. 使用 `ALTER TABLE` 命令为 `Instructors` 表添加一个新列,名为 `office_number`,类型为 `TEXT`
10. 使用 `ALTER TABLE` 命令将 `Enrollments` 表重命名为 `Student_Enrollments`
11. 创建一个名为 `Temp_Table` 的临时表(列定义不限),然后写出删除该表的命令。
* * *
## 第二部分DML (数据操作语言) - 填充与修改数据
本部分旨在考察您能否正确地插入、更新和删除数据同时遵守在DDL阶段定义的各项约束。DML操作的成功与否将直接反映您DDL设计的正确性。
**题目列表:**
1.`Instructors` 表中插入以下三位讲师的数据:
+ (1, 'Alan', 'Turing', 'alan.turing@bletchley.edu', 'B-101')
+ (2, 'Grace', 'Hopper', 'grace.hopper@yale.edu', 'C-203')
+ (3, 'John', 'von Neumann', 'john.vn@ias.edu', 'A-305')
2.`Students` 表中插入至少五名学生的数据,确保他们的 `student_id` 从101开始并拥有不同的专业。
SQL
```
INSERT INTO Students (student_id, first_name, last_name, email, major) VALUES
(101, 'Alice', 'Smith', 'alice.smith@university.edu', 'Computer Science'),
(102, 'Bob', 'Johnson', 'bob.johnson@university.edu', 'Physics'),
(103, 'Charlie', 'Brown', 'charlie.brown@university.edu', 'History'),
(104, 'Diana', 'Prince', 'diana.prince@university.edu', 'Computer Science'),
(105, 'Eve', 'Adams', 'eve.adams@university.edu', 'Physics');
```
3. 根据第一步插入的讲师ID向 `Courses` 表中插入以下课程数据:
SQL
```
INSERT INTO Courses (course_id, title, department, credits, instructor_id) VALUES
(201, 'Introduction to CS', 'CSCI', 3, 1),
(202, 'Advanced Compilers', 'CSCI', 4, 2),
(203, 'Game Theory', 'MATH', 3, 3),
(204, 'Databases', 'CSCI', 4, 1),
(205, 'Quantum Mechanics', 'PHYS', 4, NULL);
```
4. 向 `Student_Enrollments` 表注意该表已在DDL部分被重命名中插入以下选课记录
SQL
```
INSERT INTO Student_Enrollments (student_id, course_id, grade) VALUES
(101, 201, 'A'),
(101, 204, 'B+'),
(102, 205, 'A-'),
(103, 203, 'B'),
(104, 201, 'C'),
(104, 202, 'A');
```
5. **测试 `NOT NULL` 约束**:尝试向 `Instructors` 表中插入一条记录,但 `first_name` 字段留空。此操作预期会失败。请写出这条 `INSERT` 语句。
6. **测试 `UNIQUE` 约束**:尝试向 `Students` 表中插入一名新学生,其 `email` 与 'alice.smith@university.edu' 相同。此操作预期会因违反唯一性约束而失败。请写出这条 `INSERT` 语句。
7. **测试 `FOREIGN KEY` 约束**:尝试向 `Courses` 表中插入一门新课程,其 `instructor_id` 为999而这个ID在 `Instructors` 表中并不存在。此操作预期会因违反外键约束而失败。请写出这条 `INSERT` 语句。
8. Alan Turing博士更换了办公室。请使用 `UPDATE` 语句,将他的 `office_number` 更新为 'B-105'。
9. 学校决定将 'CSCI' 系的缩写统一更改为 'COMP'。请使用 `UPDATE` 语句更新 `Courses` 表中所有 'CSCI' 系的课程。
**测试 `连表查询`** 查询学生'Bob Johnson' (student_id=102) 选取的所有课程信息
10. **测试 `ON DELETE CASCADE`**:学生 'Bob Johnson' (student\_id=102) 退学。请从 `Students` 表中删除他的记录。删除后,请查询 `Student_Enrollments` 表,验证他的选课记录是否也已被自动删除。
11. **测试 `ON DELETE SET NULL`**:讲师 'Grace Hopper' (instructor\_id=2) 离职。请从 `Instructors` 表中删除她的记录。删除后,请查询 `Courses` 表,验证她所教授课程的 `instructor_id` 是否已变为 `NULL`。
12. 删除 `Student_Enrollments` 表中所有成绩为 'C' 的记录。
* * *
## 第三部分DQL (数据查询语言) - 查询与分析数据
本部分将全面考察您从数据库中检索和分析数据的能力,从简单的单表查询逐步过渡到复杂的多表聚合查询。
**题目列表:**
#### 基础检索 (1-10)
1. 查询并显示 `Students` 表中的所有列及所有行。
2. 查询所有讲师的姓(`last_name`)和名(`first_name`)。
3. 查询所有属于 'COMP' 系假设已在DML部分更新的课程名称`title`)。
4. 查询所有专业为 'Physics' 的学生信息,并按姓氏(`last_name`)升序排列。
5. 查询学分(`credits`最高的3门课程的所有信息。
6. 查询 `Students` 表中所有不重复的专业(`major`)名称。
7. 查询课程名称(`title`)中包含 'Intro' 关键字的所有课程。
8. 查询专业为 'History' 或 'Physics' 的所有学生信息。
9. 查询所有办公室编号(`office_number`)未被指定的讲师。
10. 查询所有学分在3到4之间包含3和4的课程名称和学分。
#### 聚合与分组 (11-20)
11. 统计 `Students` 表中共有多少名学生。
12. 计算 `Courses` 表中所有课程的平均学分是多少。
13. 统计选修了课程ID为201'Introduction to CS')的学生总人数。
14. 按专业(`major`)分组,统计每个专业的学生人数。
15. 按系别(`department`)分组,统计每个系别开设的课程数量。
16. 查询学生人数超过1人的专业名称以及对应的人数。
17. 按课程(`course_id`)分组,计算每门课程的平均成绩(提示:这在当前数据结构下无法直接计算,但请思考如何统计选课人数)。请统计每门课程的选课人数。
18. 查询开设课程总数超过1门的讲师ID及其开设的课程数。
19. 找出 `Courses` 表中学分最高和最低的课程的学分值。
20. 统计所有 'COMP' 系课程的总学分。
#### 连接查询与复杂查询 (21-32)
21. 使用 `INNER JOIN` 查询所有选修了 'Introduction to CS' 这门课的学生的姓和名。
22. 使用 `INNER JOIN` 连接 `Student_Enrollments` 和 `Students` 表,列出每条选课记录对应的学生全名(姓和名拼接)和成绩。
23. 使用 `INNER JOIN` 连接 `Courses` 和 `Instructors` 表,列出每位讲师的全名以及他们所教授的课程名称。
24. **`LEFT JOIN` 应用**:查询所有讲师的全名,以及他们教授的课程名称。即使某位讲师没有教授任何课程,也需要显示在结果中(其课程名称显示为 `NULL`)。
25. **`LEFT JOIN` 应用**:查询所有没有教授任何课程的讲师的全名。
26. **`LEFT JOIN` 应用**:查询所有学生的姓名,以及他们选修的课程名称。即使某个学生没有选修任何课程,也需要显示在结果中。
27. **`LEFT JOIN` 应用**:查询所有没有选修任何课程的学生的姓名。
28. 使用子查询,查询由 'Alan Turing' 教授的所有课程的名称。
29. 使用子查询,查询选修了至少一门 'COMP' 系课程的所有学生的姓名。
30. 查询选课数量最多的学生的姓名。
31. 使用 `CASE` 语句,查询所有学生的姓名和专业,并添加一列名为 `remark` 的备注。如果专业是 'Computer Science',备注为 'Tech Major';如果专业是 'Physics',备注为 'Science Major';否则备注为 'Arts & Humanities'。
32. **逻辑执行顺序理解**:尝试按专业统计学生人数,并将统计结果列命名为 `student_count`。然后,使用 `WHERE student_count > 1` 来筛选结果。这个查询会失败。请解释为什么会失败,并写出使用 `HAVING` 子句的正确查询 。  
* * *
## 第四部分TCL (事务控制语言) - 保证数据一致性
本部分旨在考察您对事务的理解以及如何使用事务来确保在执行多步操作时数据库的原子性和一致性。ACID原则是数据库管理的核心概念之一 。  
**题目列表:**
1. **基本事务**:开启一个事务,向 `Students` 表中插入一名新学生 (student\_id: 106, name: 'Frank', 'Castle', email: 'frank.castle@university.edu', major: 'Law'),然后提交事务,使更改永久生效。
2. **`ROLLBACK` 场景**:开启一个事务,删除 `Students` 表中所有专业为 'Undeclared' 的学生,然后执行 `ROLLBACK` 命令。最后,查询 `Students` 表,验证被删除的学生记录是否已恢复。
3. **原子操作场景**:学生 'Alice Smith' (student\_id: 101) 决定从 'Databases' (course\_id: 204) 这门课退选,并改选 'Game Theory' (course\_id: 203)。这个操作需要两步:从 `Student_Enrollments` 中删除一条记录,并插入一条新记录。请将这两个操作放在一个事务中,确保它们要么都成功,要么都失败。
4. **约束冲突与自动回滚**:开启一个事务。首先,将学生 'Charlie Brown' (student\_id: 103) 的专业更新为 'Philosophy'。然后,尝试插入一名新学生,但其 `email` 与已存在的学生 'Diana Prince' 冲突。由于第二步违反了 `UNIQUE` 约束会失败,整个事务会自动回滚 。请问,'Charlie Brown' 的专业最终是什么?请通过查询验证。  
5. **`SAVEPOINT` 应用**:在一个事务中,按顺序执行以下操作: a. 插入两名新的 'Art' 专业学生。 b. 创建一个名为 `after_art_students` 的保存点。 c. 插入两名新的 'Music' 专业学生。 d. 回滚到 `after_art_students` 这个保存点。 e. 提交事务。 请问,最终哪些专业的学生被永久保存到了数据库中?
6. **复杂原子操作**为一名学生办理毕业手续。这需要a) 从 `Student_Enrollments` 表中删除该学生的所有选课记录b) 从 `Students` 表中删除该学生的记录。请为学生 'Diana Prince' (student\_id: 104) 编写一个事务来完成此操作,确保数据的一致性。
7. 根据上一题的毕业操作解释ACID属性中的 'A' (Atomicity - 原子性) 是如何体现的。
8. 以 `Student_Enrollments` 表的外键约束为例解释ACID属性中的 'C' (Consistency - 一致性) 是如何保证的。
9. 解释在何种情况下,您可能会选择使用 `BEGIN IMMEDIATE TRANSACTION` 而不是默认的延迟事务(`BEGIN DEFERRED TRANSACTION`)。  
10. 一个脚本需要更新100门不同课程的学分。相比于让SQLite为每条 `UPDATE` 语句自动提交事务为什么将这100条语句包裹在一个单独的事务中会更高效
* * *
## 答案
### 第一部分DDL 答案
1. SQL
```
PRAGMA foreign_keys = ON;
```
2. SQL
```
CREATE TABLE Instructors (
instructor_id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
```
3. SQL
```
CREATE TABLE Students (
student_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
major TEXT DEFAULT 'Undeclared'
);
```
4. SQL
```
CREATE TABLE Courses (
course_id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
department TEXT NOT NULL,
credits INTEGER,
instructor_id INTEGER,
FOREIGN KEY (instructor_id) REFERENCES Instructors(instructor_id)
);
```
5. \-- 需要先删除旧表,再用新的约束创建。在实际操作中,这意味着需要备份数据。
SQL
```
-- 假设表已存在需要先删除再重建或使用ALTER TABLE在新版SQLite中
-- 为简化题目这里提供带ON DELETE SET NULL的完整创建语句
DROP TABLE Courses;
CREATE TABLE Courses (
course_id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
department TEXT NOT NULL,
credits INTEGER,
instructor_id INTEGER,
FOREIGN KEY (instructor_id) REFERENCES Instructors(instructor_id) ON DELETE SET NULL
);
```
6. SQL
```
CREATE TABLE Enrollments (
student_id INTEGER,
course_id INTEGER,
grade TEXT,
PRIMARY KEY (student_id, course_id)
);
```
7. \-- 同样,这里提供带完整外键定义的创建语句
SQL
```
DROP TABLE Enrollments;
CREATE TABLE Enrollments (
student_id INTEGER,
course_id INTEGER,
grade TEXT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES Students(student_id),
FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);
```
8. \-- 提供带ON DELETE CASCADE的完整创建语句
SQL
```
DROP TABLE Enrollments;
CREATE TABLE Enrollments (
student_id INTEGER,
course_id INTEGER,
grade TEXT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES Students(student_id) ON DELETE CASCADE,
FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);
```
9. SQL
```
ALTER TABLE Instructors ADD COLUMN office_number TEXT;
```
10. SQL
```
ALTER TABLE Enrollments RENAME TO Student_Enrollments;
```
11. SQL
```
CREATE TABLE Temp_Table (id INTEGER);
DROP TABLE Temp_Table;
```
### 第二部分DML 答案
1. SQL
```
-- 注意在DDL第9题后Instructors表结构已改变。
-- 为确保数据插入成功需要更新instructor_id=1的记录。
INSERT INTO Instructors (instructor_id, first_name, last_name, email) VALUES
(1, 'Alan', 'Turing', 'alan.turing@bletchley.edu'),
(2, 'Grace', 'Hopper', 'grace.hopper@yale.edu'),
(3, 'John', 'von Neumann', 'john.vn@ias.edu');
UPDATE Instructors SET office_number = 'B-101' WHERE instructor_id = 1;
UPDATE Instructors SET office_number = 'C-203' WHERE instructor_id = 2;
UPDATE Instructors SET office_number = 'A-305' WHERE instructor_id = 3;
```
2. SQL
```
INSERT INTO Students (student_id, first_name, last_name, email, major) VALUES
(101, 'Alice', 'Smith', 'alice.smith@university.edu', 'Computer Science'),
(102, 'Bob', 'Johnson', 'bob.johnson@university.edu', 'Physics'),
(103, 'Charlie', 'Brown', 'charlie.brown@university.edu', 'History'),
(104, 'Diana', 'Prince', 'diana.prince@university.edu', 'Computer Science'),
(105, 'Eve', 'Adams', 'eve.adams@university.edu', 'Physics');
```
3. SQL
```
INSERT INTO Courses (course_id, title, department, credits, instructor_id) VALUES
(201, 'Introduction to CS', 'CSCI', 3, 1),
(202, 'Advanced Compilers', 'CSCI', 4, 2),
(203, 'Game Theory', 'MATH', 3, 3),
(204, 'Databases', 'CSCI', 4, 1),
(205, 'Quantum Mechanics', 'PHYS', 4, NULL);
```
4. SQL
```
INSERT INTO Student_Enrollments (student_id, course_id, grade) VALUES
(101, 201, 'A'),
(101, 204, 'B+'),
(102, 205, 'A-'),
(103, 203, 'B'),
(104, 201, 'C'),
(104, 202, 'A');
```
5. SQL
```
INSERT INTO Instructors (last_name, email) VALUES ('Curie', 'marie.curie@sorbonne.edu');
-- 这将返回一个错误,因为 first_name 违反了 NOT NULL 约束。
```
6. SQL
```
INSERT INTO Students (student_id, first_name, last_name, email, major)
VALUES (106, 'Alex', 'Ray', 'alice.smith@university.edu', 'Biology');
-- 这将返回一个错误,因为 email 违反了 UNIQUE 约束。
```
7. SQL
```
INSERT INTO Courses (course_id, title, department, credits, instructor_id)
VALUES (206, 'Artificial Intelligence', 'CSCI', 4, 999);
-- 这将返回一个错误,因为 instructor_id 违反了 FOREIGN KEY 约束。
```
8. SQL
```
UPDATE Instructors SET office_number = 'B-105' WHERE instructor_id = 1;
```
9. SQL
```
UPDATE Courses SET department = 'COMP' WHERE department = 'CSCI';
```
10. SQL
```
DELETE FROM Students WHERE student_id = 102;
-- 查询验证 (应返回0行)
SELECT * FROM Student_Enrollments WHERE student_id = 102;
```
11. SQL
```
DELETE FROM Instructors WHERE instructor_id = 2;
-- 查询验证 (instructor_id应为NULL)
SELECT * FROM Courses WHERE course_id = 202;
```
12. SQL
```
DELETE FROM Student_Enrollments WHERE grade = 'C';
```
### 第三部分DQL 答案
1. `SELECT * FROM Students;`
2. `SELECT last_name, first_name FROM Instructors;`
3. `SELECT title FROM Courses WHERE department = 'COMP';`
4. `SELECT * FROM Students WHERE major = 'Physics' ORDER BY last_name ASC;`
5. `SELECT * FROM Courses ORDER BY credits DESC LIMIT 3;`
6. `SELECT DISTINCT major FROM Students;`
7. `SELECT * FROM Courses WHERE title LIKE '%Intro%';`
8. `SELECT * FROM Students WHERE major IN ('History', 'Physics');`
9. `SELECT * FROM Instructors WHERE office_number IS NULL;`
10. `SELECT title, credits FROM Courses WHERE credits BETWEEN 3 AND 4;`
11. `SELECT COUNT(*) FROM Students;`
12. `SELECT AVG(credits) FROM Courses;`
13. `SELECT COUNT(*) FROM Student_Enrollments WHERE course_id = 201;`
14. `SELECT major, COUNT(*) FROM Students GROUP BY major;`
15. `SELECT department, COUNT(*) FROM Courses GROUP BY department;`
16. `SELECT major, COUNT(*) FROM Students GROUP BY major HAVING COUNT(*) > 1;`
17. `SELECT course_id, COUNT(student_id) AS num_students FROM Student_Enrollments GROUP BY course_id;`
18. `SELECT instructor_id, COUNT(*) FROM Courses GROUP BY instructor_id HAVING COUNT(*) > 1;`
19. `SELECT MAX(credits), MIN(credits) FROM Courses;`
20. `SELECT SUM(credits) FROM Courses WHERE department = 'COMP';`
21. `SELECT s.first_name, s.last_name FROM Students s INNER JOIN Student_Enrollments se ON s.student_id = se.student_id INNER JOIN Courses c ON se.course_id = c.course_id WHERE c.title = 'Introduction to CS';`
22. `SELECT s.first_name || ' ' | | s.last_name AS full_name, se.grade FROM Students s INNER JOIN Student_Enrollments se ON s.student_id = se.student_id;`
23. `SELECT i.first_name || ' ' | | i.last_name AS instructor_name, c.title FROM Instructors i INNER JOIN Courses c ON i.instructor_id = c.instructor_id;`
24. `SELECT i.first_name, i.last_name, c.title FROM Instructors i LEFT JOIN Courses c ON i.instructor_id = c.instructor_id;`
25. `SELECT i.first_name, i.last_name FROM Instructors i LEFT JOIN Courses c ON i.instructor_id = c.instructor_id WHERE c.course_id IS NULL;`
26. `SELECT s.first_name, s.last_name, c.title FROM Students s LEFT JOIN Student_Enrollments se ON s.student_id = se.student_id LEFT JOIN Courses c ON se.course_id = c.course_id;`
27. `SELECT s.first_name, s.last_name FROM Students s LEFT JOIN Student_Enrollments se ON s.student_id = se.student_id WHERE se.course_id IS NULL;`
28. `SELECT title FROM Courses WHERE instructor_id = (SELECT instructor_id FROM Instructors WHERE first_name = 'Alan' AND last_name = 'Turing');`
29. `SELECT first_name, last_name FROM Students WHERE student_id IN (SELECT student_id FROM Student_Enrollments WHERE course_id IN (SELECT course_id FROM Courses WHERE department = 'COMP'));`
30. `SELECT s.first_name, s.last_name FROM Students s JOIN Student_Enrollments se ON s.student_id = se.student_id GROUP BY s.student_id ORDER BY COUNT(se.course_id) DESC LIMIT 1;`
31. ```sql SELECT first_name, last_name, major, CASE major WHEN 'Computer Science' THEN 'Tech Major' WHEN 'Physics' THEN 'Science Major' ELSE 'Arts & Humanities' END AS remark FROM Students; ```
32. **解释**失败是因为SQL的逻辑执行顺序是`FROM`->`WHERE`->`GROUP BY`->`HAVING`->`SELECT`->`ORDER BY`。`WHERE`子句在`SELECT`子句之前执行,因此在执行`WHERE`时,别名`student_count `尚未被创建。`HAVING`子句在`GROUP BY` 之后执行,用于过滤聚合后的结果,所以可以使用聚合函数。 **正确查询** `sql SELECT major, COUNT(*) AS student_count FROM Students GROUP BY major HAVING COUNT(*) > 1;`
### 第四部分TCL 答案
1. SQL
```
BEGIN TRANSACTION;
INSERT INTO Students (student_id, first_name, last_name, email, major)
VALUES (106, 'Frank', 'Castle', 'frank.castle@university.edu', 'Law');
COMMIT;
```
2. SQL
```
BEGIN TRANSACTION;
-- 假设有一个默认专业为'Undeclared'的学生
INSERT INTO Students (student_id, first_name, last_name, email) VALUES (107, 'John', 'Doe', 'john.doe@university.edu');
DELETE FROM Students WHERE major = 'Undeclared';
ROLLBACK;
-- 查询验证John Doe 应该仍然存在
SELECT * FROM Students WHERE student_id = 107;
```
3. SQL
```
BEGIN TRANSACTION;
DELETE FROM Student_Enrollments WHERE student_id = 101 AND course_id = 204;
INSERT INTO Student_Enrollments (student_id, course_id, grade) VALUES (101, 203, NULL);
COMMIT;
```
4. **最终专业**'History'。因为事务中的第二步失败导致整个事务自动回滚,第一步的 `UPDATE` 操作也被撤销了。 **验证查询**`SELECT major FROM Students WHERE student_id = 103;`
5. **最终结果**:只有 'Art' 专业的学生被永久保存。因为事务回滚到了保存点 `after_art_students`,这撤销了插入 'Music' 专业学生的操作,然后提交了之前的操作。
6. SQL
```
BEGIN TRANSACTION;
DELETE FROM Student_Enrollments WHERE student_id = 104;
DELETE FROM Students WHERE student_id = 104;
COMMIT;
```
7. **原子性Atomicity** 体现在毕业操作的两个步骤(删除选课记录和删除学生记录)被视为一个不可分割的单元。如果其中任何一步失败(例如,由于权限问题),整个事务将回滚,数据库将恢复到操作开始前的状态,从而避免了“学生记录已删除但选课记录仍存在”这种不一致的状态。
8. **一致性Consistency** 保证了事务将数据库从一个有效的状态转换到另一个有效的状态。`Student_Enrollments` 表的外键约束确保了任何插入的选课记录都必须关联一个真实存在的学生和一个真实存在的课程。任何试图破坏这种关系的DML操作如为不存在的学生添加选课记录都会失败从而维护了数据库在事务前后的一致性。
9. 默认的延迟事务(`DEFERRED`)直到第一次实际读写数据库时才加锁。而 `IMMEDIATE` 事务在 `BEGIN` 命令执行时就立即尝试获取一个保留锁(写锁的前置锁)。当您预知即将进行写操作,并希望尽早确保事务不会因为其他写操作而失败(返回 `SQLITE_BUSY`)时,应使用 `IMMEDIATE`。这可以减少事务中途失败的可能性。
10. 将100条 `UPDATE` 语句包裹在单个事务中更高效,主要有两个原因:
+ **减少磁盘I/O**SQLite的默认模式下每次提交事务都需要将日志文件同步到磁盘这是一个相对缓慢的操作。100次自动提交意味着100次磁盘同步。而单个事务只需要在最后 `COMMIT` 时进行一次磁盘同步大大减少了I/O开销。
+ **原子性保证**如果脚本在更新到第50条时失败单个事务可以确保所有已做的更改都被回滚数据库保持一致。而100个独立事务则会导致前49次更新已永久保存数据库处于一个不完整的中间状态。

View File

@@ -0,0 +1,271 @@
PRAGMA forien_keys = ON;
PRAGMA forien_keys;
CREATE TABLE instructors (
instructor_id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE students (
student_id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
major TEXT DEFAULT "Undeclared"
);
CREATE TABLE courses (
course_id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
department TEXT NOT NULL,
credits INTEGER,
instructor_id INTEGER ,
CONSTRAINT fk_courses_instructor FOREIGN KEY(instructor_id) REFERENCES instructors(instructor_id)
);
-- 修改 Courses 表中 instructor_id 的外键约束。要求当 Instructors 表中的某位讲师记录被删除时Courses 表中由该讲师授课的课程的 instructor_id 字段应自动被设置为 NULL 。
CREATE TABLE courses_back AS SELECT * FROM courses;
DROP TABLE courses;
CREATE TABLE courses (
course_id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
department TEXT NOT NULL,
credits INTEGER,
instructor_id INTEGER ,
CONSTRAINT fk_courses_instructor FOREIGN KEY(instructor_id) REFERENCES instructors(instructor_id) ON DELETE SET NULL
);
INSERT INTO courses SELECT * FROM courses_back;
DROP TABLE courses_back;
-- 创建一个名为 Enrollments 的连接表,用于记录学生选修课程的情况。该表应包含:
--
-- student_id: 整数类型。
--
-- course_id: 整数类型。
--
-- grade: 文本类型,用于存储成绩(如 'A', 'B+', 'C' 等)。
--
-- 将 student_id 和 course_id 的组合设置为主键(复合主键)。
create table enrollments (
student_id INTEGER NOT NULL,
course_id INTEGER NOT NULL,
grade TEXT,
-- 想象一下你的身份证号假设前6位是地区码后8位是个人编码。单看地区码无法确定你是谁一个地区有千万人单看个人编码也可能重复不同地区可能有相同个人编码。但“地区码+个人编码”的组合就能在全国范围内唯一锁定你这个人。这个组合就是“复合主键”。
-- 定义复合主键:一个学生不能多次选修同一门课程
PRIMARY KEY (student_id, course_id),
-- 要求当 Students 表中的某个学生记录被删除时,该学生所有的选课记录也应自动从 Enrollments 表中被删除(级联删除)
FOREIGN KEY (student_id) REFERENCES students(student_id) on delete cascade ,
FOREIGN KEY (course_id) REFERENCES courses(course_id)
);
PRAGMA foreign_keys = ON;
-- 使用 ALTER TABLE 命令为 Instructors 表添加一个新列,名为 office_number类型为 TEXT。
alter table instructors add column office_number TEXT;
-- 使用 ALTER TABLE 命令将 Enrollments 表重命名为 Student_Enrollments。
alter table enrollments rename to student_enrollments;
drop table student_enrollments;
drop table sqlite_master;
-- 创建一个名为 Temp_Table 的临时表(列定义不限),然后写出删除该表的命令。
-- 临时表最大的优点是无需手动管理清理,断开连接后自动消失,避免了垃圾数据累积。 wdd:有何意义?
-- 向 Instructors 表中插入以下三位讲师的数据:
--
-- (1, 'Alan', 'Turing', 'alan.turing@bletchley.edu', 'B-101')
--
-- (2, 'Grace', 'Hopper', 'grace.hopper@yale.edu', 'C-203')
--
-- (3, 'John', 'von Neumann', 'john.vn@ias.edu', 'A-305')
insert into instructors values
(1, 'Alan', 'Turing', 'alan.turing@bletchley.edu', 'B-101'),
(2, 'Grace', 'Hopper', 'grace.hopper@yale.edu', 'C-203'),
(3, 'John', 'von Neumann', 'john.vn@ias.edu', 'A-305');
INSERT INTO students VALUES
(101, 'Alice', 'Smith', 'alice.smith@university.edu', 'Computer Science'),
(102, 'Bob', 'Johnson', 'bob.johnson@university.edu', 'Physics'),
(103, 'Charlie', 'Brown', 'charlie.brown@university.edu', 'History'),
(104, 'Diana', 'Prince', 'diana.prince@university.edu', 'Computer Science'),
(105, 'Eve', 'Adams', 'eve.adams@university.edu', 'Physics');
INSERT INTO courses (course_id, title, department, credits, instructor_id) VALUES
(201, 'Introduction to CS', 'CSCI', 3, 1),
(202, 'Advanced Compilers', 'CSCI', 4, 2),
(203, 'Game Theory', 'MATH', 3, 3),
(204, 'Databases', 'CSCI', 4, 1),
(205, 'Quantum Mechanics', 'PHYS', 4, NULL);
INSERT INTO enrollments (student_id, course_id, grade) VALUES
(101, 201, 'A'),
(101, 204, 'B+'),
(102, 205, 'A-'),
(103, 203, 'B'),
(104, 201, 'C'),
(104, 202, 'A');
-- 测试 NOT NULL 约束:尝试向 Instructors 表中插入一条记录,但 first_name 字段留空。此操作预期会失败。请写出这条 INSERT 语句。
insert into instructors (instructor_id, first_name, last_name, office_number, email) values (233, null, "yes", "CS-1", "ok@qq.com");
-- 测试 FOREIGN KEY 约束:尝试向 Courses 表中插入一门新课程,其 instructor_id 为999而这个ID在 Instructors 表中并不存在。此操作预期会因违反外键约束而失败。请写出这条 INSERT 语句。
insert into courses (course_id, title, department, credits, instructor_id) values (123, "测试课程", "fts3tokenize", 12, 999);
-- Alan Turing博士更换了办公室。请使用 UPDATE 语句,将他的 office_number 更新为 'B-105'。
update instructors set office_number="B-105" where first_name="Alan" and last_name="Turing";
-- 学校决定将 'CSCI' 系的缩写统一更改为 'COMP'。请使用 UPDATE 语句更新 Courses 表中所有 'CSCI' 系的课程。
update courses set title="COMP" where title = "CSCI";
-- 测试 ON DELETE CASCADE学生 'Bob Johnson' (student_id=102) 退学。请从 Students 表中删除他的记录。删除后,请查询 Student_Enrollments 表,验证他的选课记录是否也已被自动删除
delete from students where student_id=102;
-- 连表查询 查询学生'Bob Johnson' (student_id=102) 选取的所有课程信息
select e.student_id, e.course_id, c.title, c.department, c.credits from enrollments e
INNER JOIN courses c on e.course_id = c.course_id where e.student_id = 102;
-- 测试 ON DELETE SET NULL讲师 'Grace Hopper' (instructor_id=2) 离职。请从 Instructors 表中删除她的记录。删除后,请查询 Courses 表,验证她所教授课程的 instructor_id 是否已变为 NULL。
select * from instructors i inner join courses c on i.instructor_id = c.instructor_id where i.instructor_id =2;
delete from instructors where instructor_id=2;
-- 删除 Student_Enrollments 表中所有成绩为 'C' 的记录。
select * from enrollments;
delete from enrollments where grade = 'C';
-- 查询并显示 Students 表中的所有列及所有行。
select * from students;
-- 查询所有讲师的姓last_name和名first_name
select last_name, first_name from instructors;
-- 查询所有属于 'COMP' 系假设已在DML部分更新的课程名称title
select title from courses where department = 'COMP';
-- 查询所有专业为 'Physics' 的学生信息并按姓氏last_name升序排列。
select * from students where major = 'Physics' order by last_name asc ;
-- 查询学分credits最高的3门课程的所有信息。
select * from courses order by credits desc limit 3;
-- 查询 Students 表中所有不重复的专业major名称。
select distinct major from students;
-- 查询课程名称title中包含 'Intro' 关键字的所有课程。
select * from courses where title like '%Intro%';
-- 查询专业为 'History' 或 'Physics' 的所有学生信息。
select * from students where major = 'History' or 'Physics';
-- 查询所有办公室编号office_number未被指定的讲师。
select * from instructors where office_number = '' or null;
-- 查询所有学分在3到4之间包含3和4的课程名称和学分。
select * from courses where credits between 3 and 4;
-- 统计 Students 表中共有多少名学生。
select count(*) from students;
-- 计算 Courses 表中所有课程的平均学分是多少。
select avg(credits) avg_credits from courses;
-- 统计选修了课程ID为201'Introduction to CS')的学生总人数。
select count(student_id) from enrollments where course_id = 201;
-- 按专业major分组统计每个专业的学生人数。
select count(student_id) student_count, major from students group by major;
-- 按系别department分组统计每个系别开设的课程数量。
select count(course_id) course_count, courses.department from courses group by department;
-- 查询学生人数超过1人的专业名称以及对应的人数。
SELECT major, COUNT(*) FROM Students GROUP BY major HAVING COUNT(*) > 1;;
-- 按课程course_id分组计算每门课程的平均成绩提示这在当前数据结构下无法直接计算但请思考如何统计选课人数
--
-- 请统计每门课程的选课人数。
select c.course_id, count(e.student_id), e.grade from courses c inner join enrollments e on c.course_id = e.course_id group by c.course_id;
-- 查询开设课程总数超过1门的讲师ID及其开设的课程数。
select i.instructor_id, count(c.course_id) from instructors i inner join courses c on i.instructor_id = c.instructor_id group by i.instructor_id;
-- 找出 Courses 表中学分最高和最低的课程的学分值。
-- select credits from courses group by credits ;
-- 找出学分最高和最低的值
SELECT
MAX(credits) AS highest_credits,
MIN(credits) AS lowest_credits
FROM courses;
-- 如果想要同时查看这些极值对应的课程信息
SELECT *
FROM courses
WHERE credits = (SELECT MAX(credits) FROM courses)
OR credits = (SELECT MIN(credits) FROM courses);
-- 统计所有 'COMP' 系课程的总学分。
select sum(credits) from courses where department='CSCI';
-- 使用 INNER JOIN 查询所有选修了 'Introduction to CS' 这门课的学生的姓和名。
select * from courses c inner join enrollments e on c.course_id = e.course_id inner join students s on s.student_id = e.student_id where c.title = 'Introduction to CS';
-- 使用 INNER JOIN 连接 Student_Enrollments 和 Students 表,列出每条选课记录对应的学生全名(姓和名拼接)和成绩。
select s.last_name || ' ' || s.first_name as full_name, e.grade from enrollments e inner join students s on s.student_id = e.student_id;
-- 使用 INNER JOIN 连接 Courses 和 Instructors 表,列出每位讲师的全名以及他们所教授的课程名称。
select s.last_name || ' ' || s.first_name as full_name, c.title from instructors s inner join courses c on s.instructor_id = c.instructor_id;
-- LEFT JOIN 应用:查询所有讲师的全名,以及他们教授的课程名称。即使某位讲师没有教授任何课程,也需要显示在结果中(其课程名称显示为 NULL
select s.last_name || ' ' || s.first_name as full_name, c.title from instructors s left join courses c on s.instructor_id = c.instructor_id;
-- LEFT JOIN 应用:查询所有没有教授任何课程的讲师的全名。
select s.last_name || ' ' || s.first_name as full_name from instructors s left join courses c on s.instructor_id = c.instructor_id where c.instructor_id is null;
-- LEFT JOIN 应用:查询所有学生的姓名,以及他们选修的课程名称。即使某个学生没有选修任何课程,也需要显示在结果中。
select s.last_name, s.first_name, c.title from students s left join enrollments e on s.student_id = e.student_id left join courses c on e.course_id = c.course_id;
-- LEFT JOIN 应用:查询所有没有选修任何课程的学生的姓名。
select s.last_name, s.first_name , s.student_id from students s where s.student_id not in (select student_id
from enrollments
);
-- 使用子查询,查询由 'Alan Turing' 教授的所有课程的名称。
select c.title from courses c where instructor_id = (
select instructor_id from instructors where first_name = 'Alan' and last_name = 'Turing'
);
-- 使用子查询,查询选修了至少一门 'COMP' 系课程的所有学生的姓名。
select s.first_name, s.last_name from students s where s.student_id = (
select student_id from enrollments where course_id = (
select course_id from courses where department = 'CSCI'
)
);
select count(student_id) from enrollments where course_id = (
select course_id from courses where department = 'CSCI'
);
select course_id from courses where department = 'CSCI';
-- 查询选课数量最多的学生的姓名。
select students.first_name, students.last_name
from students where student_id = (select student_id from enrollments group by student_id order by count(course_id) limit 1);
-- 使用 CASE 语句,查询所有学生的姓名和专业,并添加一列名为 remark 的备注。如果专业是 'Computer Science',备注为 'Tech Major';如果专业是 'Physics',备注为 'Science Major';否则备注为 'Arts & Humanities'。
select students.first_name, students.last_name
-- 逻辑执行顺序理解:尝试按专业统计学生人数,并将统计结果列命名为 student_count。然后使用 WHERE student_count > 1 来筛选结果。这个查询会失败。请解释为什么会失败,并写出使用 HAVING 子句的正确查询 。

View File

@@ -7,4 +7,13 @@
## SQLite引擎流程
请根据[agi_sqlite_study.md](agi_sqlite_study.md)的规范,请详细讲解SQLite中的事务事务对其他进程的读写产生何种影响请分析极端情况下的事务。SQLite中的读写隔离机制是怎样的。
请根据[agi_sqlite_study.md](agi_sqlite_study.md)的规范,请详细讲解SQLite中的事务事务对其他进程的读写产生何种影响请分析极端情况下的事务。SQLite中的读写隔离机制是怎样的。
## SQLite 题目
请根据[agi_sqlite_study.md](agi_sqlite_study.md)的规范,作为一名专业的SQL老师设计从数据格式涵盖DDL DML DQL TCL的一系列题目考察一名初学者对于SQLite学习的掌握能力
每道题目需要有序号
题目设计保持上下文连贯如DQL基于DML插入的数值DDL创建的表结构。如果数据不够请在题目中给出插入数据命令
每个大模块的题目不少于10道
DQL需要给出至少30道题目需要包含inner join, group by, having等高阶查询方法
要求答案放置于最后面,与前面的题目序号一一对应