当前位置: 首页 > news >正文

MySQL(五) - 数据连接查询和子查询操作

文章目录

  • 一、准备数据
  • 二、查询语法及解释
    • 1. 基础查询语法(单表 / 多表逗号连接)
    • 2. JOIN连接查询语法(多表关联)
  • 三、数据连接查询
    • 1. 内连接查询
    • 2. 左外连接查询
    • 3. 右外连接查询
    • 4. 交叉连接查询
    • 5. 自连接查询
  • 四、子查询
    • 1. 子查询语法
    • 2. 子查询示例
  • 五、集合运算查询
    • 1. 合并结果集(UNION 与 UNION ALL)
    • 2. 求交集(INTERSECT )
    • 3. 求差集(EXCEPT)


一、准备数据

-- 创建数据库
CREATE DATABASE IF NOT EXISTS test001;
-- 切换数据库
USE test001;
-- 删除数据表
DROP TABLE IF EXISTS student_course;
DROP TABLE IF EXISTS student;
DROP TABLE IF EXISTS course;-- 1. 学生表(主表)
CREATE TABLE IF NOT EXISTS `student` (student_id INT NOT NULL AUTO_INCREMENT COMMENT '学生ID(主键)',student_no VARCHAR(20) NOT NULL COMMENT '学号(唯一标识,如2024001)',student_name VARCHAR(50) NOT NULL COMMENT '学生姓名',gender CHAR(1) NOT NULL COMMENT '性别(男/女)',birth_date DATE NULL COMMENT '出生日期',major VARCHAR(50) NOT NULL COMMENT '所属专业',enroll_date DATE NOT NULL COMMENT '入学时间',create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',-- 列级约束PRIMARY KEY (student_id),UNIQUE KEY uk_student_no (student_no),  -- 学号唯一CHECK (gender IN ('男', '女'))  -- 限制性别只能是男或女
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
AUTO_INCREMENT = 1001  -- 学生ID从1001开始
COMMENT = '学生信息表';
-- 2. 课程表(主表)
CREATE TABLE IF NOT EXISTS `course` (course_id INT NOT NULL AUTO_INCREMENT COMMENT '课程ID(主键)',course_no VARCHAR(20) NOT NULL COMMENT '课程编号(如CS101)',course_name VARCHAR(100) NOT NULL COMMENT '课程名称',credit TINYINT NOT NULL COMMENT '学分(1-6分)',teacher_name VARCHAR(50) NOT NULL COMMENT '授课教师',course_hours INT NOT NULL COMMENT '课时数',create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',-- 列级约束PRIMARY KEY (course_id),UNIQUE KEY uk_course_no (course_no),  -- 课程编号唯一CHECK (credit BETWEEN 1 AND 6),  -- 学分范围限制CHECK (course_hours > 0)  -- 课时必须为正数
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
AUTO_INCREMENT = 101  -- 课程ID从101开始
COMMENT = '课程信息表';
-- 3. 选课表(关系表,关联学生表和课程表)
CREATE TABLE IF NOT EXISTS `student_course` (id INT NOT NULL AUTO_INCREMENT COMMENT '选课记录ID(主键)',student_id INT NOT NULL COMMENT '学生ID(外键关联学生表)',course_id INT NOT NULL COMMENT '课程ID(外键关联课程表)',select_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '选课时间',score DECIMAL(5,2) NULL COMMENT '课程成绩(0-100分,NULL表示未考试)',is_valid TINYINT NOT NULL DEFAULT 1 COMMENT '是否有效(1-有效,0-已退课)',-- 表级约束PRIMARY KEY (id),-- 联合唯一约束:同一学生不能重复选同一门课UNIQUE KEY uk_stu_course (student_id, course_id),-- 外键约束:关联学生表CONSTRAINT fk_sc_student FOREIGN KEY (student_id)REFERENCES `student`(student_id)ON DELETE CASCADE  -- 学生记录删除时,关联的选课记录自动删除ON UPDATE CASCADE,  -- 学生ID更新时,选课记录同步更新-- 外键约束:关联课程表CONSTRAINT fk_sc_course FOREIGN KEY (course_id)REFERENCES `course`(course_id)ON DELETE CASCADE  -- 课程记录删除时,关联的选课记录自动删除ON UPDATE CASCADE,-- 检查约束:成绩范围限制(0-100分)CONSTRAINT chk_score CHECK (score IS NULL OR (score BETWEEN 0 AND 100)),-- 检查约束:is_valid只能是0或1CONSTRAINT chk_is_valid CHECK (is_valid IN (0, 1))
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '学生选课关系表';-- 插入 500 条学生数据
DELIMITER $$
CREATE PROCEDURE InsertStudents()
BEGINDECLARE i INT DEFAULT 1;DECLARE gender CHAR(1);DECLARE year_start DATE;DECLARE birth DATE;DECLARE major_name VARCHAR(50);-- 常见专业列表SET @majors = '计算机科学与技术,软件工程,电子信息工程,数学与应用数学,物理学,化学,生物技术,机械工程,自动化,通信工程,''土木工程,环境工程,经济学,金融学,会计学,法学,汉语言文学,英语,新闻传播学,临床医学,护理学';WHILE i <= 500 DOSET gender = IF(RAND() > 0.5, '男', '女');-- 随机入学年份:2020 - 2024SET year_start = MAKEDATE(2020 + FLOOR(RAND() * 5), 1 + FLOOR(RAND() * 365));-- 出生日期:入学时 17~23 岁SET birth = DATE_SUB(year_start, INTERVAL FLOOR(17 + RAND() * 7) YEAR);-- 随机选择专业SET major_name = ELT(CEILING(RAND() * 21), '计算机科学与技术','软件工程','电子信息工程','数学与应用数学','物理学','化学','生物技术','机械工程','自动化','通信工程','土木工程','环境工程','经济学','金融学','会计学','法学','汉语言文学','英语','新闻传播学','临床医学','护理学');INSERT INTO `student` (student_no, student_name, gender, birth_date, major, enroll_date)VALUES (CONCAT('20', LPAD(FLOOR(RAND() * 90 + 24), 2, '0'), LPAD(i, 3, '0')),  -- 如 2024001CONCAT(ELT(CEILING(RAND() * 10), '张','李','王','刘','陈','杨','黄','赵','周','吴'),ELT(CEILING(RAND() * 10), '伟','芳','敏','静','勇','磊','洋','娟','强','军')),gender,birth,major_name,year_start);SET i = i + 1;END WHILE;
END$$
DELIMITER ;-- 执行并清理
CALL InsertStudents();
DROP PROCEDURE IF EXISTS InsertStudents;-- 插入 500 条课程数据
DELIMITER $$
CREATE PROCEDURE InsertCourses()
BEGINDECLARE i INT DEFAULT 1;DECLARE course_name_prefix VARCHAR(50);DECLARE teacher_name VARCHAR(50);SET @prefixes = '高等数学,线性代数,概率统计,C语言程序设计,Java编程,Python数据分析,数据结构,算法设计,操作系统,''计算机网络,数据库原理,软件工程,电路分析,模拟电子技术,数字逻辑,信号与系统,电磁场与波,自动控制原理,''大学物理,大学化学,马克思主义基本原理,中国近代史纲要,英语读写,体育健康,艺术鉴赏,心理学导论,经济学基础';WHILE i <= 500 DOSET course_name_prefix = ELT(CEILING(RAND() * 27),'高等数学','线性代数','概率统计','C语言程序设计','Java编程','Python数据分析','数据结构','算法设计','操作系统','计算机网络','数据库原理','软件工程','电路分析','模拟电子技术','数字逻辑','信号与系统','电磁场与波','自动控制原理','大学物理','大学化学','马克思主义基本原理','中国近代史纲要','英语读写','体育健康','艺术鉴赏','心理学导论','经济学基础');SET teacher_name = CONCAT(ELT(CEILING(RAND() * 10), '张','李','王','刘','陈','杨','黄','赵','周','吴'),'老师');INSERT INTO `course` (course_no, course_name, credit, teacher_name, course_hours)VALUES (CONCAT('CS', LPAD(i, 3, '0')),  -- CS001, CS002...CONCAT(course_name_prefix, '(', CEILING(RAND() * 10), '期)'),CEILING(RAND() * 6),  -- 1~6 学分teacher_name,CASE CEILING(RAND() * 5)WHEN 1 THEN 32WHEN 2 THEN 48WHEN 3 THEN 64WHEN 4 THEN 80ELSE 96END);SET i = i + 1;END WHILE;
END$$
DELIMITER ;-- 执行并清理
CALL InsertCourses();
DROP PROCEDURE IF EXISTS InsertCourses;-- 插入 500 条不重复的选课记录
DELIMITER $$
CREATE PROCEDURE InsertStudentCourse()
BEGINDECLARE i INT DEFAULT 1;DECLARE sid INT;DECLARE cid INT;DECLARE retry_count INT DEFAULT 0;DECLARE max_retries INT DEFAULT 2000;WHILE i <= 500 DO-- 随机学生ID:1001 ~ 1500SET sid = FLOOR(1001 + RAND() * 500);-- 随机课程ID:101 ~ 600SET cid = FLOOR(101 + RAND() * 500);-- 尝试插入,跳过已存在的组合BEGINDECLARE CONTINUE HANDLER FOR 1062 BEGIN END; -- 忽略重复键错误INSERT INTO `student_course` (student_id, course_id, select_time, score, is_valid)VALUES (sid,cid,NOW() - INTERVAL FLOOR(RAND() * 365) DAY,  -- 近一年内选课IF(RAND() > 0.2, ROUND(40 + RAND() * 60, 2), NULL), -- 80% 有成绩IF(RAND() > 0.1, 1, 0) -- 90% 有效,10% 已退课);-- 成功插入才计数SET i = i + 1;END;SET retry_count = retry_count + 1;IF retry_count > max_retries THENSIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '插入失败:可能可用的 (student_id, course_id) 组合已耗尽';END IF;END WHILE;
END$$
DELIMITER ;-- 执行并清理
CALL InsertStudentCourse();
DROP PROCEDURE IF EXISTS InsertStudentCourse;-- 检查每张表的数据量
SELECT 'student' AS table_name, COUNT(*) AS count FROM student
UNION ALL
SELECT 'course' AS table_name, COUNT(*) AS count FROM course
UNION ALL
SELECT 'student_course' AS table_name, COUNT(*) AS count FROM student_course;

二、查询语法及解释

1. 基础查询语法(单表 / 多表逗号连接)

SELECT [ALL DISTINCT] <字段名> [AS 别名1] [, <字段名2> [AS 别名2]]
FROM <表名1> [AS1别名] [, <表名2> [AS2别名], ...]
[WHERE <检索条件>]
[GROUP BY <列名1> [HAVING <条件表达式>]]
[ORDER BY <列名2> [ASC DESC]];

2. JOIN连接查询语法(多表关联)

SELECT [ALL DISTINCT] 字段名1 [AS 别名1], 字段名2 [AS 别名2], ...
FROM 表名1 [AS1别名]
[INNER LEFT RIGHT [OUTER] CROSS] JOIN 表名2 [AS2别名]
ON 条件;
类别细分项作用/说明关键特征
FROM 与 JOIN 子句- 左表(表名1)连接的基础表,作为查询的"基准"数据源在 LEFT JOIN 中会保留所有记录;在 INNER JOIN 中仅保留匹配记录
- 右表(表名2)需与左表关联的表,提供补充数据在 RIGHT JOIN 中会保留所有记录;在 INNER JOIN 中仅保留匹配记录
- 连接方式定义两表记录的匹配规则,决定结果集中包含哪些记录不同连接类型直接影响结果集的范围(如交集、左表全量、右表全量等)
连接类型INNER JOIN(内连接)返回两表中同时满足 ON 条件的记录仅保留交集,无匹配的记录不显示
LEFT [OUTER] JOIN(左外连接)返回左表所有记录 + 右表中满足 ON 条件的匹配记录左表无匹配时,右表字段显示 NULL;右表不影响左表记录的完整性
RIGHT [OUTER] JOIN(右外连接)返回右表所有记录 + 左表中满足 ON 条件的匹配记录右表无匹配时,左表字段显示 NULL;左表不影响右表记录的完整性
CROSS JOIN(交叉连接)返回两表的笛卡尔积(所有可能的记录组合)无需 ON 条件,结果行数 = 左表行数 × 右表行数,通常需配合 WHERE 过滤冗余数据
ON 子句关联条件定义两表记录的匹配规则(如 表1.字段A = 表2.字段B过滤无效组合,仅保留逻辑关联的记录;JOIN 必须配合 ON 条件(除 CROSS JOIN 外)

三、数据连接查询

1. 内连接查询

内连接(INNER JOIN)只会返回两表中同时满足连接条件的记录,相当于两个表的交集。

示例1:查询所有学生的选课信息,包括学生姓名、课程名称和成绩

SELECT s.student_name, c.course_name, sc.score
FROM student s
INNER JOIN student_course sc ON s.student_id = sc.student_id
INNER JOIN course c ON sc.course_id = c.course_id;

SELECT s.student_name, c.course_name, sc.score
FROM student s, student_course sc, course c
WHERE s.student_id = sc.student_id AND  sc.course_id = c.course_id;

在这里插入图片描述

示例2:查询选修了“计算机科学与技术”专业课程的学生姓名和课程名称

SELECT s.student_name, c.course_name
FROM student s
INNER JOIN student_course sc ON s.student_id = sc.student_id
INNER JOIN course c ON sc.course_id = c.course_id
WHERE s.major = '计算机科学与技术';

SELECTs.major,s.student_name,c.course_name 
FROMstudent s,student_course sc,course c 
WHEREs.student_id = sc.student_id AND sc.course_id = c.course_id AND s.major = '计算机科学与技术';

在这里插入图片描述

2. 左外连接查询

左外连接(LEFT OUTER JOIN / LEFT JOIN)以左表为基准,返回左表的所有记录,同时匹配右表中满足条件的记录。若右表无匹配,右表字段用 NULL 填充。

示例1:查询所有学生的基本信息及他们的选课成绩(含没选课的学生)。

SELECT s.student_no, s.student_name, s.major, c.course_name, sc.score
FROM student s
LEFT JOIN student_course sc ON s.student_id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.course_id
ORDER BY s.student_id;

在这里插入图片描述

示例2:统计每个学生的选课数量(没选课的学生计数为 0)。

SELECT s.student_name, COUNT(sc.course_id) AS select_course_count
FROM student s
LEFT JOIN student_course sc ON s.student_id = sc.student_id
GROUP BY s.student_no, s.student_name
ORDER BY select_course_count DESC;

在这里插入图片描述

3. 右外连接查询

右外连接(RIGHT OUTER JOIN / RIGHT JOIN)以右表为基准,返回右表的所有记录,同时匹配左表中满足条件的记录。若左表无匹配,左表字段用 NULL 填充。

示例1:查询所有课程的信息及选该课的学生成绩(含没被选的课程)。

SELECT c.course_no, c.course_name, c.teacher_name, s.student_name, sc.score
FROM student s
RIGHT JOIN student_course sc ON s.student_id = sc.student_id
RIGHT JOIN course c ON sc.course_id = c.course_id
ORDER BY c.course_id;

在这里插入图片描述

示例2:统计每门课程的选课人数(没被选的课程计数为 0)。

SELECT c.course_name, COUNT(sc.student_id) AS student_count
FROM student s
RIGHT JOIN student_course sc ON s.student_id = sc.student_id
RIGHT JOIN course c ON sc.course_id = c.course_id
GROUP BY c.course_id, c.course_name, c.credit
ORDER BY student_count DESC;

在这里插入图片描述

4. 交叉连接查询

交叉连接(CROSS JOIN) 用于返回两个表的笛卡尔积,即左表的每一行与右表的每一行都形成一条记录,结果集的行数 = 左表行数 × 右表行数。 交叉连接本身不使用 ON 条件过滤,通常需要配合 WHERE 子句筛选有效数据,否则结果可能包含大量冗余记录。

示例1:生成“所有学生与所有课程的组合”(例如:用于初始化选课系统的可选列表)。

SELECT s.student_name, c.course_name
FROM student s
CROSS JOIN course c;

在这里插入图片描述

示例2:在特定条件下筛选交叉组合(例如:为“计算机科学与技术”专业的学生匹配所有“计算机类”课程(课程名含“计算机”))。

SELECT s.student_name, c.course_name
FROM student s
CROSS JOIN course c
WHERE s.major = '计算机科学与技术' AND c.course_name LIKE '%计算机%';

在这里插入图片描述

5. 自连接查询

自连接是表与自身的连接,即把一张表当作两张不同的表(通过别名区分),用于查询表中“具有关联关系的记录”。

示例:查找同专业的学生。

SELECTs1.student_name AS 学生A, s1.student_no 学号A,s2.student_name AS 学生B, s2.student_no 学号B,s1.major AS 共同专业
FROM student s1
INNER JOIN student s2 ON s1.major = s2.major
WHERE s1.student_id < s2.student_id;

SELECTs1.student_name AS 学生A, s1.student_no 学号A,s2.student_name AS 学生B, s2.student_no 学号B,s1.major AS 共同专业
FROM student s1, student s2
WHERE s1.major = s2.major AND s1.student_id < s2.student_id;

在这里插入图片描述


四、子查询

子查询指嵌套在主查询中的查询语句,通常用于为主查询提供“条件值”或“数据集”,可放在 SELECTFROMWHERE 等子句中。

1. 子查询语法

子查询需用 括号 () 包裹,按返回结果可分为三类,语法结构对应不同场景:

子查询类型返回结果适用场景语法示例片段
标量子查询单个值(1行1列)为主查询提供单个条件值(如对比、赋值)WHERE 主表字段 = (SELECT 字段 FROM 子表 WHERE 条件)
列子查询单个列的多个值(N行1列)配合 IN/NOT IN/ANY/ALL 筛选WHERE 主表字段 IN (SELECT 字段 FROM 子表 WHERE 条件)
表子查询多个列的数据集(N行M列)作为主查询的“临时表”,需起别名FROM (SELECT 字段1,字段2 FROM 子表 WHERE 条件) AS 临时表名

2. 子查询示例

示例1:标量子查询,查询与“刘娟”同专业的所有学生姓名和学号。

SELECT student_name, student_no
FROM student
WHERE major = (SELECT majorFROM studentWHERE student_name = '刘娟' AND student_no = 2078009
);

在这里插入图片描述

示例2:列子查询,查询“数据结构”课程的所有选课学生姓名。

SELECT s.student_name
FROM student s
INNER JOIN student_course sc ON s.student_id = sc.student_id
WHERE sc.course_id IN (SELECT course_idFROM courseWHERE course_name LIKE '%数据结构%'
);

在这里插入图片描述

示例3:表子查询,查询“计算机科学与技术”专业学生的平均成绩作为临时表,查询每门课程的平均成绩。

SELECT temp.course_name, AVG(temp.score) AS avg_score
FROM (SELECT s.student_id, s.student_name, c.course_name, sc.scoreFROM student sINNER JOIN student_course sc ON s.student_id = sc.student_idINNER JOIN course c ON sc.course_id = c.course_idWHERE s.major = '计算机科学与技术'
) AS temp
GROUP BY temp.course_name
ORDER BY avg_score DESC;

在这里插入图片描述

示例4:EXISTS 子查询(判断是否存在记录),查询“至少选了1门课且成绩≥90分”的学生姓名。

EXISTS 判断子查询是否返回结果(无需关注具体值,只看“有无”),效率比 IN 更高。

SELECT student_name
FROM student s
WHERE EXISTS (SELECT 1FROM student_course scWHERE sc.student_id = s.student_id AND sc.score >= 90
);

在这里插入图片描述


五、集合运算查询

集合运算用于将两个或多个查询结果集进行组合,主要包括 UNIONUNION ALLINTERSECTEXCEPT 四种,它们的核心是对结果集进行"合并"或"筛选"操作。

运算类型语法格式作用说明关键特性
UNION查询1 UNION 查询2合并两个查询结果,自动去除重复记录结果集列数和数据类型必须一致;会进行去重操作,性能略低
UNION ALL查询1 UNION ALL 查询2合并两个查询结果,保留所有记录(包括重复)列要求同上;不去重,性能优于 UNION
INTERSECT查询1 INTERSECT 查询2返回两个结果集的交集(同时存在于两个结果集的记录)MySQL 不直接支持,需用 JOIN 替代
EXCEPT查询1 EXCEPT 查询2返回两个结果集的差集(在查询1中但不在查询2中的记录)MySQL 不直接支持,需用 LEFT JOIN + IS NULL 替代

1. 合并结果集(UNION 与 UNION ALL)

查询"计算机科学与技术"专业的学生和"数据库原理"课程的选课学生,合并结果并去重。

SELECT student_id, student_name, '计算机专业学生' AS type
FROM student
WHERE major = '计算机科学与技术'
UNION
SELECT s.student_id, s.student_name, '数据库选课学生' AS type
FROM student s
INNER JOIN student_course sc ON s.student_id = sc.student_id
INNER JOIN course c ON sc.course_id = c.course_id
WHERE c.course_name LIKE '%数据库原理%';

在这里插入图片描述

2. 求交集(INTERSECT )

查询既选了"数据库"又选了"计算机"的学生姓名。

SELECT s1.student_name
FROM (SELECT s.student_id, s.student_nameFROM student sINNER JOIN student_course sc ON s.student_id = sc.student_idINNER JOIN course c ON sc.course_id = c.course_idWHERE c.course_name LIKE '%数据库%'
) s1
INNER JOIN (SELECT s.student_idFROM student sINNER JOIN student_course sc ON s.student_id = sc.student_idINNER JOIN course c ON sc.course_id = c.course_idWHERE c.course_name LIKE '%计算机%'
) s2 ON s1.student_id = s2.student_id;

在这里插入图片描述

3. 求差集(EXCEPT)

查询选了课但成绩未录入(score为NULL)的学生,排除已经退课的(is_valid=0)。

SELECT s.student_name, sc.id AS 选课记录ID
FROM student_course sc
INNER JOIN student s ON sc.student_id = s.student_id
WHERE sc.score IS NULL
AND NOT EXISTS (SELECT 1FROM student_course sc2WHERE sc2.id = sc.idAND sc2.is_valid = 0
);

在这里插入图片描述

http://www.dtcms.com/a/549099.html

相关文章:

  • STM32——WWDG
  • STM32-音频播放
  • 前端学习:选择器的类别
  • 运输网站建设wordpress 不同page
  • Qt的Debug版本和Release版本有什么区别?
  • Docker使用【容器】
  • 行业电子商务网站建设房地产网站开发公司电话
  • LangChain 提示模板之少样本示例(二)
  • Product Hunt 每日热榜 | 2025-10-30
  • Spring MVC核心概念
  • 鸿蒙HDF框架源码分析
  • Springboot旅游管理系统8cx8xy5m(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • ppt免费制作网站如何建设网站山东济南兴田德润官网
  • 如何获取网站根目录链接诸城做网站的
  • GPT-0: Attention+Transformer+可视化
  • 告别“人眼扫描”:EasyGBS智能搜索功能助力重塑海量视频监控管理效率
  • 【ubuntu】ubuntu系统如何快速删除当前用户的配置
  • dz地方门户网站制作南昌seo招聘信息
  • 灵犀科技网站开发湖南网站建设的公司排名
  • 沈阳装修公司网站建设做网站只用前端知识可以吗
  • ELK es+logstash
  • Java 大视界 -- Java 大数据在智能医疗远程康复数据管理与康复方案个性化定制实战(430)
  • 【C#】XtraMessageBox(DevExpress)与MessageBox(WinForms 标准库)的区别
  • 石家庄物流网站建设北京公司网站建设价格
  • 网络编程入门
  • 每日一个C语言知识:C 错误处理
  • 基础建设的网站有哪些内容网页制作基础教程费
  • 建站工具哪个好用深圳网站建设定制平台
  • 2048游戏笔记3 游戏开始与结束 cocos3.8.7
  • 【AI WorkFow】n8n 源码分析-核心结构体设计思考(六)