MySQL复合查询

EMP表(Employee Table,员工表)

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
);
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,部门表)

create table DEPT(
deptno int,
dname varchar(20),
loc varchar(20));
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字段的记录

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的部门名,员工名和工资

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的上级领导的编号和姓名

使用子查询

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)

使用多表查询

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同一部门的员工

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自
己的。

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的所有员工的工资高的员工的姓名、工资和部门号

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的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门
的员工)

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本人

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子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用
案例:
显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资

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)

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

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:使用多表

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:使用子查询

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的执行结果,可以使用集合操作符 unionunion all

3.5.1.union

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

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

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

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

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 子句对两种表形成的笛卡儿积进行筛选,前面学习的查询都是内连接,也是在开发过程中使用的最多的连接查询。

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

案例

-- 用前面的写法
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。

select 字段名 from 表名1 left join 表名2 on 连接条件
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);
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 |
+------+-------+

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

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。

select 字段 from 表名1 right join 表名2 on 连接条件
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)
### 关于 MySQL 复合查询的教程 复合查询是指通过 `UNION` 或子查询等方式组合多个 SQL 查询的结果集。以下是有关如何构建和执行 MySQL 中复杂查询的一些指导: #### 使用 UNION 进行复合查询 可以通过 `UNION` 和 `UNION ALL` 来合并两个或更多 `SELECT` 语句的结果集。注意,`UNION` 默认会去除重复项,而 `UNION ALL` 则保留所有记录。 ```sql -- 示例:使用 UNION 合并两个查询结果 SELECT tutorial_id, tutorial_title FROM tutorials_tbl WHERE tutorial_author = 'Abdul S' UNION SELECT tutorial_id, tutorial_title FROM tutorials_tbl WHERE tutorial_author = 'John Poul'; ``` 此示例展示了如何从同一表的不同条件中提取数据并将它们合并成单个结果集[^1]。 #### 子查询的应用 子查询是在另一个 SQL 语句内部嵌套的一个查询。它可以用于过滤、计算或其他操作。 ```sql -- 示例:基于子查询筛选数据 SELECT * FROM tutorials_tbl WHERE tutorial_id IN ( SELECT tutorial_id FROM tutorials_tbl WHERE submission_date >= '2007-05-01'); ``` 在此例子中,外部查询依赖于内部查询返回的一组 ID 值来进一步缩小范围[^1]。 #### JOIN 的高级应用 当涉及跨多张表格的数据关联时,可以利用各种类型的联接(JOIN),如 INNER JOIN、LEFT JOIN 等实现更加复杂的逻辑处理。 ```sql -- 示例:INNER JOIN 实现两表间匹配字段联合检索 SELECT t.tutorial_id, u.user_name FROM tutorials_tbl AS t INNER JOIN users_tbl AS u ON t.author_id = u.id; ``` 这里假设存在一张名为 `users_tbl` 的用户信息表,并且每篇教程都有对应的作者ID (`author_id`) 可以用来建立联系关系[^3]。 #### 性能优化建议 对于大规模数据上的复杂查询,应当考虑索引设计以及避免不必要的全表扫描等问题。此外,在实际项目开发过程中还可以借助缓存机制或者分页技术提升响应速度[^2]。 ### 提供的相关资源链接 虽然当前文档未具体提及某些在线学习资料地址,但是可以根据以上介绍自行搜索关键词比如 “advanced mysql queries examples pdf”,“mysql complex join operations step by step guide” 获取更多信息源[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值