MySQL复合查询

EMP表(Employee Table,员工表)

cpp 复制代码
create table EMP(
empno int primary key,
ename varchar(50),
job varchar(50),
mgr int, comment "上级"
hiredate datetime, comment "入职日期"
sal decimal(10, 2), comment "薪水"
comm decimal(10, 2), comment "奖金"
deptno int
);
cpp 复制代码
INSERT INTO EMP VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17 00:00:00', 800.00, NULL, 20);
INSERT INTO EMP VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20 00:00:00', 1600.00, 300.00, 30);
INSERT INTO EMP VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22 00:00:00', 1250.00, 500.00, 30);
INSERT INTO EMP VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02 00:00:00', 2975.00, NULL, 20);
INSERT INTO EMP VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28 00:00:00', 1250.00, 1400.00, 30);
INSERT INTO EMP VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01 00:00:00', 2850.00, NULL, 30);
INSERT INTO EMP VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09 00:00:00', 2450.00, NULL, 10);
INSERT INTO EMP VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19 00:00:00', 3000.00, NULL, 20);
INSERT INTO EMP VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17 00:00:00', 5000.00, NULL, 10);
INSERT INTO EMP VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08 00:00:00', 1500.00, 0.00, 30);
INSERT INTO EMP VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23 00:00:00', 1100.00, NULL, 20);
INSERT INTO EMP VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03 00:00:00', 950.00, NULL, 30);
INSERT INTO EMP VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03 00:00:00', 3000.00, NULL, 20);
INSERT INTO EMP VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23 00:00:00', 1300.00, NULL, 10);

DEPT表(Department Table,部门表)

cpp 复制代码
create table DEPT(
deptno int,
dname varchar(20),
loc varchar(20));
cpp 复制代码
INSERT INTO DEPT (deptno, dname, loc) VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT (deptno, dname, loc) VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT (deptno, dname, loc) VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT (deptno, dname, loc) VALUES (40, 'OPERATIONS', 'BOSTON');

多表查询是指在一个 SQL 查询语句中,从两个或两个以上的表中获取数据。在关系型数据库中,表与表之间通常通过外键等关联关系连接,多表查询可以将这些关联表中的数据结合起来,满足更复杂的查询需求。常见的多表查询方式有以下几种:

1.交叉连接

交叉连接会返回两个表中所有行的组合,也称为笛卡尔积。如果表 A 有 m 行,表 B 有 n 行,那么交叉连接的结果会有 m×n 行。

显示雇员名、雇员工资以及所在部门的名字因为上面的数据来自EMPDEPT表,因此要联合查询

其实我们只要emp表中的deptno = dept表中的deptno字段的记录

cpp 复制代码
mysql> select EMP.ename, EMP.sal, DEPT.dname from EMP,DEPT 
     > where EMP.deptno = DEPT.deptno;
+--------+---------+------------+
| ename  | sal     | dname      |
+--------+---------+------------+
| SMITH  |  800.00 | RESEARCH   |
| ALLEN  | 1600.00 | SALES      |
| WARD   | 1250.00 | SALES      |
| JONES  | 2975.00 | RESEARCH   |
| MARTIN | 1250.00 | SALES      |
| BLAKE  | 2850.00 | SALES      |
| CLARK  | 2450.00 | ACCOUNTING |
| SCOTT  | 3000.00 | RESEARCH   |
| KING   | 5000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES      |
| ADAMS  | 1100.00 | RESEARCH   |
| JAMES  |  950.00 | SALES      |
| FORD   | 3000.00 | RESEARCH   |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
14 rows in set (0.00 sec)

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

cpp 复制代码
mysql> select dname, ename, sal from EMP, DEPT 
    -> where EMP.deptno=DEPT.deptno and EMP.deptno = 10;
+------------+--------+---------+
| dname      | ename  | sal     |
+------------+--------+---------+
| ACCOUNTING | CLARK  | 2450.00 |
| ACCOUNTING | KING   | 5000.00 |
| ACCOUNTING | MILLER | 1300.00 |
+------------+--------+---------+

2.自连接

自连接是指一个表与自身进行连接,通常用于处理表中存在层次关系或自身关联的情况。

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

使用子查询

cpp 复制代码
mysql> select empno, ename from EMP 
    -> where mgr = (select empno from EMP where ename='FORD');
+-------+-------+
| empno | ename |
+-------+-------+
|  7369 | SMITH |
+-------+-------+
1 row in set (0.00 sec)

使用多表查询

cpp 复制代码
mysql> select leader.empno, leader.ename from EMP leader, EMP worker 
    -> where worker.ename = "FORD" and leader.empno = worker.empno;
+-------+-------+
| empno | ename |
+-------+-------+
|  7902 | FORD  |
+-------+-------+
1 row in set (0.00 sec)

3.子查询

子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询

3.1.单行子查询

返回一行记录的子查询

显示SMITH同一部门的员工

cpp 复制代码
mysql> select * from EMP where deptno = (select deptno from EMP where ename = 'SMITH');
+-------+-------+---------+------+---------------------+---------+------+--------+
| empno | ename | job     | mgr  | hiredate            | sal     | comm | deptno |
+-------+-------+---------+------+---------------------+---------+------+--------+
|  7369 | SMITH | CLERK   | 7902 | 1980-12-17 00:00:00 |  800.00 | NULL |     20 |
|  7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL |     20 |
|  7788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL |     20 |
|  7876 | ADAMS | CLERK   | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
|  7902 | FORD  | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL |     20 |
+-------+-------+---------+------+---------------------+---------+------+--------+
5 rows in set (0.00 sec)

只返回 deptno 这 1 列,且结果是 20(只有 1 行),属于单行子查询。

3.2.多行子查询

返回多行记录的子查询

in关键字;查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10自
己的。

cpp 复制代码
mysql> select ename, job, sal, deptno from EMP 
    -> where job in (select job from EMP where deptno = 10) and deptno <> 10;
+-------+---------+---------+--------+
| ename | job     | sal     | deptno |
+-------+---------+---------+--------+
| JONES | MANAGER | 2975.00 |     20 |
| BLAKE | MANAGER | 2850.00 |     30 |
| SMITH | CLERK   |  800.00 |     20 |
| ADAMS | CLERK   | 1100.00 |     20 |
| JAMES | CLERK   |  950.00 |     30 |
+-------+---------+---------+--------+
5 rows in set (0.01 sec)

all关键字;显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

cpp 复制代码
mysql> select ename, sal, deptno from EMP
    -> where sal > all(select sal from EMP where deptno=30);
+-------+---------+--------+
| ename | sal     | deptno |
+-------+---------+--------+
| JONES | 2975.00 |     20 |
| SCOTT | 3000.00 |     20 |
| KING  | 5000.00 |     10 |
| FORD  | 3000.00 |     20 |
+-------+---------+--------+
4 rows in set (0.00 sec)

any关键字;显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门
的员工)

cpp 复制代码
mysql> select ename, sal, deptno from EMP 
    -> where sal > any(select sal from EMP where deptno=30);
+--------+---------+--------+
| ename  | sal     | deptno |
+--------+---------+--------+
| ALLEN  | 1600.00 |     30 |
| WARD   | 1250.00 |     30 |
| JONES  | 2975.00 |     20 |
| MARTIN | 1250.00 |     30 |
| BLAKE  | 2850.00 |     30 |
| CLARK  | 2450.00 |     10 |
| SCOTT  | 3000.00 |     20 |
| KING   | 5000.00 |     10 |
| TURNER | 1500.00 |     30 |
| ADAMS  | 1100.00 |     20 |
| FORD   | 3000.00 |     20 |
| MILLER | 1300.00 |     10 |
+--------+---------+--------+
12 rows in set (0.00 sec)

只返回 deptno 这 1 列,但结果可能有 10、20、30 多行(多个部门编号),属于多行子查询。

3.3.多列子查询

单行子查询是指子查询只返回单列,单行数据;多行子查询是指返回单列多行数据,都是针对单列而言

的,而多列子查询则是指查询返回多个列数据的子查询语句

案例:查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人

cpp 复制代码
mysql> select * from EMP where 
    -> (deptno,job)=(select deptno, job from EMP where ename="SMITH") 
    -> and ename <> 'SMITH';
+-------+-------+-------+------+---------------------+---------+------+--------+
| empno | ename | job   | mgr  | hiredate            | sal     | comm | deptno |
+-------+-------+-------+------+---------------------+---------+------+--------+
|  7876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
+-------+-------+-------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)

3.4.在from中使用子查询

子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用

案例:
显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资

cpp 复制代码
mysql> select EMP.ename, EMP.deptno, sal 
    -> from EMP, (select deptno, avg(sal) asal from EMP group by deptno) tmp 
    -> where EMP.sal > tmp.asal and EMP.deptno=tmp.deptno;
+-------+--------+---------+
| ename | deptno | sal     |
+-------+--------+---------+
| ALLEN |     30 | 1600.00 |
| JONES |     20 | 2975.00 |
| BLAKE |     30 | 2850.00 |
| SCOTT |     20 | 3000.00 |
| KING  |     10 | 5000.00 |
| FORD  |     20 | 3000.00 |
+-------+--------+---------+
6 rows in set (0.00 sec)

查找每个部门工资最高的人的姓名、工资、部门、最高工资

cpp 复制代码
mysql> select EMP.ename, EMP.sal, EMP.deptno, ms 
    -> from EMP, (select max(sal) ms, deptno from EMP group by deptno) tmp 
    -> where EMP.deptno=tmp.deptno and EMP.sal=tmp.ms;
+-------+---------+--------+---------+
| ename | sal     | deptno | ms      |
+-------+---------+--------+---------+
| BLAKE | 2850.00 |     30 | 2850.00 |
| SCOTT | 3000.00 |     20 | 3000.00 |
| KING  | 5000.00 |     10 | 5000.00 |
| FORD  | 3000.00 |     20 | 3000.00 |
+-------+---------+--------+---------+
4 rows in set (0.00 sec)

显示每个部门的信息(部门名,编号,地址)和人员数量

方法1:使用多表

cpp 复制代码
mysql> select DEPT.dname, DEPT.deptno, DEPT.loc, count(*) 
    -> from DEPT, EMP where DEPT.deptno=EMP.deptno 
    -> group by DEPT.dname, DEPT.deptno, DEPT.loc;
+------------+--------+----------+----------+
| dname      | deptno | loc      | count(*) |
+------------+--------+----------+----------+
| ACCOUNTING |     10 | NEW YORK |        3 |
| RESEARCH   |     20 | DALLAS   |        5 |
| SALES      |     30 | CHICAGO  |        6 |
+------------+--------+----------+----------+
3 rows in set (0.00 sec)

方法2:使用子查询

cpp 复制代码
mysql> select DEPT.dname, DEPT.deptno, DEPT.loc, num 
    -> from DEPT, (select deptno, count(*) num from EMP group by deptno) tmp 
    -> where DEPT.deptno=tmp.deptno;
+------------+--------+----------+-----+
| dname      | deptno | loc      | num |
+------------+--------+----------+-----+
| ACCOUNTING |     10 | NEW YORK |   3 |
| RESEARCH   |     20 | DALLAS   |   5 |
| SALES      |     30 | CHICAGO  |   6 |
+------------+--------+----------+-----+
3 rows in set (0.00 sec)

3.5.合并查询

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

3.5.1.union

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

案例:将工资大于2500或职位是MANAGER的人找出来

cpp 复制代码
mysql> select ename, sal, job from EMP where sal>2500 union
    -> select ename, sal, job from EMP where job='MANAGER';
+-------+---------+-----------+
| ename | sal     | job       |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| SCOTT | 3000.00 | ANALYST   |
| KING  | 5000.00 | PRESIDENT |
| FORD  | 3000.00 | ANALYST   |
| CLARK | 2450.00 | MANAGER   |
+-------+---------+-----------+
6 rows in set (0.00 sec)

3.5.2.union all

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

cpp 复制代码
mysql> select ename, sal, job from EMP where sal>2500 union all
    -> select ename, sal, job from EMP where job='MANAGER';
+-------+---------+-----------+
| ename | sal     | job       |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| SCOTT | 3000.00 | ANALYST   |
| KING  | 5000.00 | PRESIDENT |
| FORD  | 3000.00 | ANALYST   |
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| CLARK | 2450.00 | MANAGER   |
+-------+---------+-----------+
8 rows in set (0.00 sec)

4.内连接和外连接

4.1.内连接

在数据库查询中,内连接(INNER JOIN)和外连接(OUTER JOIN)是用于关联多个表时的两种常用连接方式,它们的主要区别在于如何处理不匹配的记录。

内连接只返回两个表中匹配条件的记录,即只包含在两个表中都存在对应关系的数据。内连接实际上就是利用 where 子句对两种表形成的笛卡儿积进行筛选,前面学习的查询都是内连接,也是在开发过程中使用的最多的连接查询。

cpp 复制代码
select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件;

案例

cpp 复制代码
-- 用前面的写法
select ename, dname from EMP, DEPT where EMP.deptno=DEPT.deptno 
and ename='SMITH';
-- 用标准的内连接写法
select ename, dname from EMP inner join DEPT on EMP.deptno=DEPT.deptno 
and ename='SMITH';

4.2.外连接

外连接会返回至少一个表中 满足连接条件的记录,根据保留哪个表的不匹配记录,又分为:左外连接和右外连接。

4.2.1.左外连接

返回左表中的所有记录,以及右表中匹配条件的记录。如果右表中没有匹配项,则显示 NULL。

cpp 复制代码
select 字段名 from 表名1 left join 表名2 on 连接条件
cpp 复制代码
create table stu (id int, name varchar(30)); -- 学生表
insert into stu values(1,'jack'),(2,'tom'),(3,'kity'),(4,'nono');
create table exam (id int, grade int); -- 成绩表
insert into exam values(1, 56),(2,76),(11, 8);
cpp 复制代码
mysql> select * from stu;
+------+------+
| id   | name |
+------+------+
|    1 | jack |
|    2 | tom  |
|    3 | kity |
|    4 | nono |
+------+------+

mysql> select * from exam;
+------+-------+
| id   | grade |
+------+-------+
|    1 |    56 |
|    2 |    76 |
|   11 |     8 |
+------+-------+

查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来

cpp 复制代码
mysql> select * from stu left join exam on stu.id=exam.id;
+------+------+------+-------+
| id   | name | id   | grade |
+------+------+------+-------+
|    1 | jack |    1 |    56 |
|    2 | tom  |    2 |    76 |
|    3 | kity | NULL |  NULL |
|    4 | nono | NULL |  NULL |
+------+------+------+-------+
4 rows in set (0.00 sec)

4.2.2.右外连接

返回右表中的所有记录,以及左表中匹配条件的记录。如果左表中没有匹配项,则显示 NULL。

cpp 复制代码
select 字段 from 表名1 right join 表名2 on 连接条件
cpp 复制代码
mysql> select * from stu right join exam on stu.id=exam.id;
+------+------+------+-------+
| id   | name | id   | grade |
+------+------+------+-------+
|    1 | jack |    1 |    56 |
|    2 | tom  |    2 |    76 |
| NULL | NULL |   11 |     8 |
+------+------+------+-------+
3 rows in set (0.00 sec)
相关推荐
hygge9992 小时前
MySQL 全体系深度解析(存储引擎、事务、日志、MVCC、锁、索引、执行计划、复制、调优)
数据库·经验分享·mysql·adb·面试
百***87442 小时前
【MySQL】SQL菜鸟教程(一)
sql·mysql·oracle
百***65952 小时前
PON架构(全光网络)
网络·数据库·架构
Databend2 小时前
Databend SQL 存储过程使用指南
数据库
冒泡的肥皂2 小时前
说下数据存储
数据库·后端·mysql
BullSmall3 小时前
MySQL全面安全加固实战指南
mysql·安全·adb
码河漫步3 小时前
win11安装mysql社区版数据库
数据库·mysql
Wang's Blog3 小时前
MySQL: 存储引擎深度解析:Memory与Federated的特性与应用场景
数据库·mysql
学习中的程序媛~3 小时前
Spring 事务(@Transactional)与异步(@Async / CompletableFuture)结合的陷阱与最佳实践
java·数据库·sql