MySQL复合查询、表的连接问题记录

 更新时间:2026年09月04日 10:10:46   作者:Sunday不上发条  
MySQL的‌复合查询‌就是把多表查询、自连接、子查询、合并查询组合起来用,‌表的连接‌则是复合查询的基础,核心是‌内连接‌和‌外连接(左/右连接)‌,下面按实际使用场景拆开讲,感兴趣的朋友一起看看吧

一、多表查询

1.1 数据准备

准备三张表:

  • 部门表
  • 员工表
  • 工资表
CREATE database IF NOT EXISTS `scott_data` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `scott_data`;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
  `deptno` int(2) unsigned zerofill NOT NULL COMMENT '部门编号',
  `dname` varchar(14) DEFAULT NULL COMMENT '部门名称',
  `loc` varchar(13) DEFAULT NULL COMMENT '部门所在地点'
);
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
  `empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
  `ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
  `job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
  `mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
  `hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
  `comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
  `deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
  `grade` int(11) DEFAULT NULL COMMENT '等级',
  `losal` int(11) DEFAULT NULL COMMENT '此等级最低工资',
  `hisal` int(11) DEFAULT NULL COMMENT '此等级最高工资'
);
insert into dept (deptno, dname, loc)
values (10, 'ACCOUNTING', 'NEW YORK')
,(20, 'RESEARCH', 'DALLAS')
,(30, 'SALES', 'CHICAGO')
,(40, 'OPERATIONS', 'BOSTON');
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20)
,(7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30)
,(7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30)
,(7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20)
,(7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30)
,(7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30)
,(7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10)
,(7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20)
,(7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10)
,(7844, 'TURNER', 'SALESMAN', 7698,'1981-09-08', 1500, 0, 30)
,(7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20)
,(7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30)
,(7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20)
,(7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);
insert into salgrade (grade, losal, hisal) values (1, 700, 1200)
,(2, 1201, 1400)
,(3, 1401, 2000)
,(4, 2001, 3000)
,(5, 3001, 9999);

可以先在 Linux 中创建 scott_data.sql 文件,将上面的代码拷贝进去,然后,mysql -u root -p scott_data < ./scott_data.sql 在MySQL 中创建scott_data数据库并建表。

1.2 笛卡尔积

select ... from student, class;

对 student 和 class 作笛卡尔积,即让 student 的每一行与 class 每一行进行拼接。

对于需要进行多表查询的场景,通过笛卡尔积可以更方便的解决问题。

1.3 多表查询

案例:

(1)显示雇员名、雇员工资以及所在部门的名字

select ename, sal, dname from emp, dept where emp.deptno = dept.deptno;

(2)显示部门号为 10 的部门名,员工名和工资

select dept.dname, ename, sal from emp, dept where emp.deptno = dept.deptno AND emp.deptno = 10;

(3)显示各个员工的姓名,工资,及工资级别

select ename, sal, grade from emp, salgrade where sal between losal and hisal;

1.4 自连接

自连接是指同一张表进行笛卡尔积。

注意:直接进行笛卡尔积不行,需要对表取不同的别名!

select ... from student t1, student t2;

案例:显示员工 FORD 的上级领导的编号和姓名

+--------+-------+
| empno  | ename |
+--------+-------+
| 007566 | JONES |
+--------+-------+
  • 使用子查询
select empno, ename from emp where empno = (select mgr from emp where ename = 'FORD');
  • 使用多表查询
select t2.empno, t2.ename from emp t1, emp t2 where t1.ename = 'FORD' AND t1.mgr = t2.empno;

二、子查询

子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询,即把 select 查询的结果作为筛选条件。

2.1 单行子查询

子查询的结果为一行。

  • 显示 SMITH 同一部门的员工
select * from emp where deptno = (select deptno from emp where ename = 'SMITH');

select deptno from emp where ename = 'SMITH' 的结果作为筛选条件。

2.2 多行子查询

子查询返回多条记录。

  • in 关键字;查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10自己的
select ename, job, sal, deptno from emp where job in(select job from emp where deptno = 10) 
AND deptno <> 10;
  • all 关键字;显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
select ename, sal, deptno from emp where sal > all(select sal from emp where deptno = 30);
  • any 关键字;显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门的员工)
select ename, sal, deptno from emp where sal > any(select sal from emp where deptno = 30);

2.3 多列子查询

单行和多行子查询的返回值都是单列的,多列子查询的返回值为多列数据。

  • 查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人
select ename from emp where (deptno, job) = (select deptno, job from emp where ename = 'SMITH') 
AND ename <> 'SMITH';

2.4 在from子句中使用子查询

在 from 子句中使用子查询,即将子查询的结果作为一张临时表使用。

案例:

  • 显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资
select ename, emp.deptno, sal, tmp.平均工资 from emp, 
(select deptno, avg(sal) 平均工资 from emp group by deptno) tmp 
where emp.deptno = tmp.deptno AND emp.sal > tmp.平均工资;
+-------+--------+---------+--------------+
| ename | deptno | sal     | 平均工资      |
+-------+--------+---------+--------------+
| ALLEN |     30 | 1600.00 |  1566.666667 |
| JONES |     20 | 2975.00 |  2175.000000 |
| BLAKE |     30 | 2850.00 |  1566.666667 |
| SCOTT |     20 | 3000.00 |  2175.000000 |
| KING  |     10 | 5000.00 |  2916.666667 |
| FORD  |     20 | 3000.00 |  2175.000000 |
+-------+--------+---------+--------------+

为了方便操作,可以对子查询得到的临时表进行重命名

  • 查找每个部门工资最高的人的姓名、工资、部门、最高工资
select ename, sal, emp.deptno, tmp.最高工资 from emp, 
(select deptno, max(sal) 最高工资 from emp group by deptno) tmp 
where emp.deptno = tmp.deptno AND emp.sal = tmp.最高工资;
  • 显示每个部门的信息(部门名,编号,地址)和人员数量
    • 使用子查询
select dname, dept.deptno, loc, tmp.人员数量 from dept, 
(select deptno, count(*) 人员数量 from emp groupby deptno) tmp 
where dept.deptno = tmp.deptno;
- 使用多表查询
select dept.deptno, dname, loc, count(*) from dept, emp 
where emp.deptno = dept.deptno group by dept.deptnno, dname, loc;

2.5 合并查询

在实际应用中,为了合并多个 select 的执行结果,可以使用集合操作符 unionunion all

(1)union

该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。

  • 将工资大于2500或职位是MANAGER的人找出来
mysql> select ename, sal, job from emp where sal > 2500 union
    -> select ename, sal, job from emp where job = 'MANAGER';

(2)union all

该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。

select ename, sal, job from emp where sal > 2500 union all 
select ename, sal, job from emp where job = 'MANAGANAGER';

三、表的内外连接

3.1 内连接

内连接就是“笛卡尔积”,也是开发过程中使用最多的连接查询方式。

语法:

select ... from 表1 inner join 表2 on 连接条件 and 其他条件;

案例:显示SMITH的名字和部门名称

-- 之前的写法:
select ename, dname from emp, dept 
where emp.deptno = dept.deptno AND emp.ename = 'SMITH';
-- 标准写法:
select ename, dname from emp inner join dept 
on emp.deptno = dept.deptno AND emp.ename = 'SMITH';

3.2 外连接

外连接分为左外连接和右外连接。

准备两张表对外连接进行测试:

  • student
  • exam

3.2.1 左外连接

左外连接在两张表进行连接时会保留左表全部数据去匹配右表数据,右表缺失列补 NULL

语法:

select ... from 左表 left join 右表 on 连接条件;

案例:student 作为左表匹配 exam。

select * from student left join exam on student.id = exam.id;
+------+------+------+-------+
| id   | name | id   | grade |
+------+------+------+-------+
|    1 | jack |    1 |    56 |
|    2 | tom  |    2 |    76 |
|    3 | kity | NULL |  NULL |
|    4 | nono | NULL |  NULL |
+------+------+------+-------+

3.2.2 右外连接

右外连接在两张表进行连接时会保留右表全部数据去匹配左表数据,左表缺失列补 NULL

语法:

select ... from 左表 right join  右表 on 连接条件;

案例:把所有的成绩都显示出来,即使这个成绩没有学生与它对应,也要显示出来

select * from student right join exam on student.id = exam.id;
+------+------+------+-------+
| id   | name | id   | grade |
+------+------+------+-------+
|    1 | jack |    1 |    56 |
|    2 | tom  |    2 |    76 |
| NULL | NULL |   11 |     8 |
+------+------+------+-------+

内连接与外连接的区别为:

  • 内连接只会返回两张表中同时满足条件的行
  • 外连接对于指定表,全部显示,未匹配到的补 NULL

到此这篇关于MySQL复合查询、表的连接的文章就介绍到这了,更多相关mysql内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL infobright的安装步骤

    MySQL infobright的安装步骤

    这篇文章主要介绍了MySQL infobright的安装步骤,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下
    2021-04-04
  • MySQL数据库基础篇SQL窗口函数示例解析教程

    MySQL数据库基础篇SQL窗口函数示例解析教程

    这篇文章主要为大家介绍了MySQL数据库基础篇之窗口函数示例解析教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-10-10
  • 解决MySQL Varchar 类型尾部空格的问题

    解决MySQL Varchar 类型尾部空格的问题

    这篇文章主要介绍了MySQL Varchar 类型尾部空格,在这里需要注意的是 binary 排序规则的 pad 属性为 NO PAD,这里其实不是个例外,因为 char、varchar 和 text 类型都归类为 nonbinary,感兴趣的朋友跟随小编一起学习下吧
    2022-04-04
  • MySQL 深分页查询优化实践与经验分享

    MySQL 深分页查询优化实践与经验分享

    文章总结了在企业级项目中优化深分页查询的经验,包括执行计划分析、索引优化和游标分页改写,通过复合索引和游标分页,可以显著提高查询性能,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • MySQL存储过程in、out和inout参数示例和总结

    MySQL存储过程in、out和inout参数示例和总结

    这篇文章主要给大家介绍了关于MySQL存储过程in、out和inout参数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • mysql 5.7 zip 文件在 windows下的安装教程详解

    mysql 5.7 zip 文件在 windows下的安装教程详解

    这篇文章主要介绍了mysql 5.7 zip 文件在 windows下的安装步骤,首先我们需要先下载mysql最新版本然后解压文件夹,本文介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09
  • MySQL主从复制断开的常用修复方法

    MySQL主从复制断开的常用修复方法

    这篇文章主要介绍了MySQL主从复制断开的常用修复方法,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下
    2021-04-04
  • MySQL不支持InnoDB的解决方法

    MySQL不支持InnoDB的解决方法

    在OpenSUSE下装上MySQL后,发现无法选择添加事务支持数据引擎InnoDB。
    2009-11-11
  • Mysql实现递归树查询的使用示例

    Mysql实现递归树查询的使用示例

    Mysql我们可以使用递归查询来构建一个递归树,本文就来介绍一下Mysql实现递归树查询的使用示例,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • MySQL深度分页(千万级数据量如何快速分页)

    MySQL深度分页(千万级数据量如何快速分页)

    后端开发中经常需要分页展示,个时候就需要用到MySQL的LIMIT关键字。LIMIT在数据量大的时候极可能造成的一个问题就是深度分页。本文就介绍一下解决方法,感兴趣的可以了解一下
    2021-07-07

最新评论