可以先看上篇文章,包含对数据库创建、表创建、表数据插入。SQL查询从入门到起飞。
SQL从入门到起飞:完整学习数据库与100+练习题
下面我将补充数据库操作(DDL、DML)的练习题目,包括创建数据库、表、插入、更新、删除等操作。
DDL(数据定义语言)练习
数据库操作
- 创建一个名为
university_db
的数据库 - 查看所有数据库列表
- 切换到
university_db
数据库 - 删除
university_db
数据库(练习后恢复)
表操作
- 创建一个
faculty
表,包含字段:faculty_id(主键), faculty_name, dean_name, established_year - 修改
students
表,添加一个phone_number
字段 - 修改
students
表,将scholarship
字段改为DECIMAL(10,2)
- 为
teachers
表添加一个索引在department_id
字段上 - 创建一个
course_prerequisites
表来管理课程先修关系 - 为
library
表添加一个唯一约束在isbn
字段上 - 创建一个视图
student_course_grades
,显示学生姓名、课程名称和成绩 - 创建一个存储过程
get_student_info
,根据学生ID返回学生信息 - 创建一个触发器,在删除学生前检查是否有相关记录
- 为
scores
表添加检查约束,确保成绩在0-100之间 - 创建一个临时表存储成绩优秀的学生信息
DML(数据操作语言)练习
插入操作
- 向
faculty
表插入3个学院的数据 - 向
students
表插入5名新学生的数据 - 向
teachers
表插入3名新教师的数据 - 向
courses
表插入2门新课程的数据 - 向
enrollments
表插入10条选课记录 - 向
scores
表插入相应的成绩记录 - 使用INSERT...SELECT语句将计算机系学生复制到一个新表
- 批量插入多条图书馆藏书记录
- 插入一条借阅记录,包括借书和预计归还日期
- 使用多行插入语法一次性插入多个社团信息
更新操作
- 将所有教师的工资增加10%
- 将计算机系学生的奖学金增加1000元
- 将"数据库系统"课程的成绩全部提高5分(不超过100分)
- 更新所有逾期未还书的罚款金额(每天0.5元)
- 将2022年入学学生的院系改为新成立的"人工智能"系
- 将所有教授的职称更新为"正教授"
- 将没有投影仪的教室添加投影仪设备
- 将选修了"Java程序设计"且成绩低于60分的学生成绩改为60分
- 将所有社团的预算增加20%
- 将年龄大于20岁的学生的奖学金减少500元
删除操作
- 删除所有成绩为0的记录
- 删除没有学生选修的课程
- 删除借阅记录中已归还且无罚款的记录
- 删除没有成员参加的社团
- 删除2023年之前的所有活动记录
- 使用事务删除一个学生及其所有相关记录
- 删除重复的选课记录(同一学生同一课程多次选课)
- 删除没有安排教室的课程安排记录
- 删除可用副本数为0的图书记录
- 删除工资低于平均工资的教师记录
综合操作练习
- 创建一个新表
alumni
,并将已毕业学生的数据转移至此表 - 使用MERGE语句同步学生表和校友表的数据
- 使用CASE语句更新学生奖学金等级(90分以上A等,80-89分B等,其他C等)
- 使用事务处理借书操作(减少可用副本数,添加借阅记录)
- 使用游标遍历所有学生并更新其年龄字段
- 创建一个函数计算学生的GPA(平均学分绩点)
- 创建一个存储过程用于分页查询学生信息
- 使用临时表统计每个院系的学生成绩分布
- 使用公用表表达式(CTE)查询学生的选课路径
- 使用窗口函数计算每个学生的成绩排名
完整示例代码
DDL操作示例
sql
-- 1. 创建数据库
CREATE DATABASE IF NOT EXISTS university_db;
USE university_db;
-- 5. 创建院系表
CREATE TABLE faculty (
faculty_id INT PRIMARY KEY AUTO_INCREMENT,
faculty_name VARCHAR(100) NOT NULL,
dean_name VARCHAR(100),
established_year INT
);
-- 6. 修改学生表添加电话号码字段
ALTER TABLE students ADD COLUMN phone_number VARCHAR(15);
-- 7. 修改学生表奖学金字段类型
ALTER TABLE students MODIFY COLUMN scholarship DECIMAL(10,2);
-- 9. 创建课程先修关系表
CREATE TABLE course_prerequisites (
prerequisite_id INT PRIMARY KEY AUTO_INCREMENT,
course_id INT NOT NULL,
required_course_id INT NOT NULL,
minimum_grade DECIMAL(5,2) DEFAULT 60.00,
FOREIGN KEY (course_id) REFERENCES courses(course_id),
FOREIGN KEY (required_course_id) REFERENCES courses(course_id)
);
-- 10. 为图书馆表添加ISBN唯一约束
ALTER TABLE library ADD CONSTRAINT uc_isbn UNIQUE (isbn);
-- 11. 创建学生成绩视图
CREATE VIEW student_course_grades AS
SELECT
s.student_id,
s.first_name,
s.last_name,
c.course_name,
sc.midterm_score,
sc.final_score,
sc.total_score
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id
JOIN scores sc ON e.enrollment_id = sc.enrollment_id;
-- 12. 创建获取学生信息的存储过程
DELIMITER //
CREATE PROCEDURE get_student_info(IN student_id INT)
BEGIN
SELECT
s.student_id,
s.first_name,
s.last_name,
s.email,
s.enrollment_date,
d.department_name,
s.scholarship
FROM students s
JOIN departments d ON s.department_id = d.department_id
WHERE s.student_id = student_id;
END //
DELIMITER ;
-- 13. 创建删除学生前的检查触发器
DELIMITER //
CREATE TRIGGER before_student_delete
BEFORE DELETE ON students
FOR EACH ROW
BEGIN
DECLARE enrollment_count INT;
DECLARE borrow_count INT;
-- 检查是否有选课记录
SELECT COUNT(*) INTO enrollment_count
FROM enrollments
WHERE student_id = OLD.student_id;
-- 检查是否有借阅记录
SELECT COUNT(*) INTO borrow_count
FROM borrow_records
WHERE student_id = OLD.student_id AND return_date IS NULL;
IF enrollment_count > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete student with course enrollments';
END IF;
IF borrow_count > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete student with borrowed books';
END IF;
END //
DELIMITER ;
-- 14. 添加成绩检查约束
ALTER TABLE scores
ADD CONSTRAINT chk_score_range
CHECK (
(midterm_score BETWEEN 0 AND 100 OR midterm_score IS NULL) AND
(final_score BETWEEN 0 AND 100 OR final_score IS NULL) AND
(assignment_score BETWEEN 0 AND 100 OR assignment_score IS NULL)
);
-- 15. 创建临时表存储优秀学生
CREATE TEMPORARY TABLE top_students AS
SELECT
s.student_id,
s.first_name,
s.last_name,
AVG(sc.total_score) AS average_score
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN scores sc ON e.enrollment_id = sc.enrollment_id
GROUP BY s.student_id, s.first_name, s.last_name
HAVING AVG(sc.total_score) >= 90;
DML操作示例
sql
-- 16. 插入学院数据
INSERT INTO faculty (faculty_name, dean_name, established_year) VALUES
('工程学院', '张院长', 2000),
('理学院', '李院长', 1995),
('文学院', '王院长', 1985);
-- 17. 插入新学生数据
INSERT INTO students (first_name, last_name, email, enrollment_date, date_of_birth, department_id, scholarship, phone_number) VALUES
('唐', '雨', 'tangyu@email.com', '2023-09-01', '2004-12-03', 1, 2000.00, '13800138000'),
('宋', '阳', 'songyang@email.com', '2023-09-01', '2004-07-21', 2, 1500.00, '13900139000'),
('秦', '雪', 'qinxue@email.com', '2023-09-01', '2004-03-15', 3, 0.00, '13700137000'),
('许', '明', 'xuming@email.com', '2023-09-01', '2004-09-28', 4, 1000.00, '13600136000'),
('何', '静', 'hejing@email.com', '2023-09-01', '2004-11-11', 5, 2500.00, '13500135000');
-- 18. 插入新教师数据
INSERT INTO teachers (first_name, last_name, email, hire_date, department_id, title, salary) VALUES
('韩', '教授', 'han@email.com', '2021-06-10', 1, '副教授', 72000.00),
('董', '老师', 'dong@email.com', '2022-02-15', 2, '讲师', 58000.00),
('曹', '教授', 'cao@email.com', '2019-08-20', 3, '教授', 88000.00);
-- 19. 插入新课程数据
INSERT INTO courses (course_code, course_name, credits, teacher_id, department_id) VALUES
('CS501', '人工智能', 4, 1, 1),
('MATH401', '数值分析', 3, 10, 2);
-- 20. 插入选课记录
INSERT INTO enrollments (student_id, course_id, enrollment_date, semester, academic_year) VALUES
(16, 14, '2023-09-05', 'Fall', 2023),
(17, 15, '2023-09-05', 'Fall', 2023),
(18, 14, '2023-09-06', 'Fall', 2023),
(19, 15, '2023-09-06', 'Fall', 2023),
(20, 14, '2023-09-07', 'Fall', 2023),
(16, 1, '2023-09-07', 'Fall', 2023),
(17, 5, '2023-09-08', 'Fall', 2023),
(18, 8, '2023-09-08', 'Fall', 2023),
(19, 10, '2023-09-09', 'Fall', 2023),
(20, 11, '2023-09-09', 'Fall', 2023);
-- 26. 更新教师工资增加10%
UPDATE teachers SET salary = salary * 1.1;
-- 27. 更新计算机系学生奖学金
UPDATE students s
JOIN departments d ON s.department_id = d.department_id
SET s.scholarship = s.scholarship + 1000
WHERE d.department_name = '计算机科学';
-- 28. 更新数据库系统课程成绩
UPDATE scores sc
JOIN enrollments e ON sc.enrollment_id = e.enrollment_id
JOIN courses c ON e.course_id = c.course_id
SET sc.final_score = LEAST(sc.final_score + 5, 100)
WHERE c.course_name = '数据库系统';
-- 29. 更新逾期罚款
UPDATE borrow_records
SET fine_amount = DATEDIFF(CURDATE(), due_date) * 0.5
WHERE return_date IS NULL AND due_date < CURDATE();
-- 36. 删除成绩为0的记录
DELETE FROM scores WHERE total_score = 0;
-- 37. 删除没有学生选修的课程
DELETE FROM courses
WHERE course_id NOT IN (SELECT DISTINCT course_id FROM enrollments);
-- 41. 使用事务删除学生及相关记录
START TRANSACTION;
-- 首先删除相关记录
DELETE FROM scores WHERE enrollment_id IN (
SELECT enrollment_id FROM enrollments WHERE student_id = 15
);
DELETE FROM enrollments WHERE student_id = 15;
DELETE FROM borrow_records WHERE student_id = 15;
DELETE FROM club_members WHERE student_id = 15;
DELETE FROM event_participants WHERE student_id = 15;
-- 最后删除学生
DELETE FROM students WHERE student_id = 15;
COMMIT;
综合操作示例
sql
-- 46. 创建校友表并转移数据
CREATE TABLE alumni (
alumni_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
enrollment_date DATE,
graduation_date DATE,
department_id INT,
current_job VARCHAR(100),
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
-- 转移已毕业学生数据(假设2022年入学学生已毕业)
INSERT INTO alumni (first_name, last_name, email, enrollment_date, graduation_date, department_id)
SELECT first_name, last_name, email, enrollment_date, '2023-06-30', department_id
FROM students
WHERE enrollment_date < '2023-01-01';
-- 47. 使用MERGE语句同步数据(MySQL不支持MERGE,使用INSERT...ON DUPLICATE KEY UPDATE)
INSERT INTO alumni (first_name, last_name, email, enrollment_date, graduation_date, department_id)
SELECT first_name, last_name, email, enrollment_date, '2023-06-30', department_id
FROM students
WHERE enrollment_date < '2023-01-01'
ON DUPLICATE KEY UPDATE
first_name = VALUES(first_name),
last_name = VALUES(last_name),
graduation_date = VALUES(graduation_date),
department_id = VALUES(department_id);
-- 48. 使用CASE更新奖学金等级
UPDATE students s
JOIN (
SELECT
s.student_id,
CASE
WHEN AVG(sc.total_score) >= 90 THEN 'A'
WHEN AVG(sc.total_score) >= 80 THEN 'B'
ELSE 'C'
END AS scholarship_grade
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN scores sc ON e.enrollment_id = sc.enrollment_id
GROUP BY s.student_id
) grades ON s.student_id = grades.student_id
SET s.scholarship =
CASE grades.scholarship_grade
WHEN 'A' THEN 5000
WHEN 'B' THEN 3000
ELSE 1000
END;
-- 49. 使用事务处理借书操作
START TRANSACTION;
-- 检查书籍是否可用
SELECT available_copies INTO @available FROM library WHERE book_id = 5;
IF @available > 0 THEN
-- 减少可用副本数
UPDATE library SET available_copies = available_copies - 1 WHERE book_id = 5;
-- 添加借阅记录
INSERT INTO borrow_records (student_id, book_id, borrow_date, due_date)
VALUES (1, 5, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 30 DAY));
COMMIT;
ELSE
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Book not available';
END IF;
-- 51. 创建计算GPA的函数
DELIMITER //
CREATE FUNCTION calculate_gpa(student_id INT) RETURNS DECIMAL(3,2)
READS SQL DATA
BEGIN
DECLARE total_points DECIMAL(5,2);
DECLARE total_credits INT;
DECLARE gpa DECIMAL(3,2);
SELECT
SUM(CASE
WHEN sc.total_score >= 90 THEN 4.0 * c.credits
WHEN sc.total_score >= 80 THEN 3.0 * c.credits
WHEN sc.total_score >= 70 THEN 2.0 * c.credits
WHEN sc.total_score >= 60 THEN 1.0 * c.credits
ELSE 0
END),
SUM(c.credits)
INTO total_points, total_credits
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id
JOIN scores sc ON e.enrollment_id = sc.enrollment_id
WHERE s.student_id = student_id;
IF total_credits > 0 THEN
SET gpa = total_points / total_credits;
ELSE
SET gpa = 0;
END IF;
RETURN gpa;
END //
DELIMITER ;
-- 52. 创建分页查询存储过程
DELIMITER //
CREATE PROCEDURE get_students_paginated(
IN page_number INT,
IN page_size INT,
OUT total_students INT,
OUT total_pages INT
)
BEGIN
-- 获取总学生数
SELECT COUNT(*) INTO total_students FROM students;
-- 计算总页数
SET total_pages = CEIL(total_students / page_size);
-- 分页查询学生
SELECT *
FROM students
ORDER BY student_id
LIMIT page_size
OFFSET (page_number - 1) * page_size;
END //
DELIMITER ;
-- 53. 使用临时表统计成绩分布
CREATE TEMPORARY TABLE grade_distribution AS
SELECT
d.department_name,
COUNT(*) AS total_students,
SUM(CASE WHEN gpa >= 3.5 THEN 1 ELSE 0 END) AS excellent,
SUM(CASE WHEN gpa >= 2.5 AND gpa < 3.5 THEN 1 ELSE 0 END) AS good,
SUM(CASE WHEN gpa >= 1.5 AND gpa < 2.5 THEN 1 ELSE 0 END) AS average,
SUM(CASE WHEN gpa < 1.5 THEN 1 ELSE 0 END) AS poor
FROM (
SELECT
s.student_id,
s.department_id,
calculate_gpa(s.student_id) AS gpa
FROM students s
) student_gpas
JOIN departments d ON student_gpas.department_id = d.department_id
GROUP BY d.department_name;
-- 54. 使用CTE查询选课路径
WITH RECURSIVE course_path AS (
-- 基础查询:没有先修课程的课程
SELECT
course_id,
course_name,
CAST(course_name AS CHAR(500)) AS path
FROM courses
WHERE prerequisite_id IS NULL
UNION ALL
-- 递归查询:有先修课程的课程
SELECT
c.course_id,
c.course_name,
CONCAT(cp.path, ' -> ', c.course_name)
FROM courses c
JOIN course_path cp ON c.prerequisite_id = cp.course_id
)
SELECT * FROM course_path;
-- 55. 使用窗口函数计算成绩排名
SELECT
s.student_id,
s.first_name,
s.last_name,
c.course_name,
sc.total_score,
RANK() OVER (PARTITION BY c.course_id ORDER BY sc.total_score DESC) AS course_rank,
RANK() OVER (PARTITION BY s.department_id ORDER BY sc.total_score DESC) AS department_rank
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id
JOIN scores sc ON e.enrollment_id = sc.enrollment_id;
学习建议
- 先掌握基本的DDL语句(CREATE, ALTER, DROP)
- 熟练使用DML语句(INSERT, UPDATE, DELETE)
- 学习事务处理(BEGIN, COMMIT, ROLLBACK)
- 掌握约束的使用(主键、外键、唯一、检查约束)
- 学习视图、存储过程、函数和触发器的创建与使用
- 练习复杂的数据操作和业务逻辑实现
- 学习性能优化技巧(索引、查询优化)
- 定期备份和恢复数据库
通过这些完整的数据库操作练习,你将全面掌握SQL从基础到高级的所有操作,真正实现从入门到起飞的目标!