08.MySQL复合查询详解
08.MySQL复合查询详解
基本查询回顾
多表查询
自连接
子查询
单行子查询
多行子查询
多列子查询
在FROM子句中使用子查询
合并查询
MySQL复合查询详解
基本查询回顾
在正式进入复合查询之前,先简单回顾一下MySQL的基本查询操作。假设我们有三张表:员工表(emp
)、部门表(dept
)和工资等级表(salgrade
)。这些表的结构和数据如下:
员工表(emp
)
empno | ename | job | mgr | hiredate | sal | comm | deptno |
---|---|---|---|---|---|---|---|
7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 |
7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 |
… | … | … | … | … | … | … | … |
部门表(dept
)
deptno | dname | loc |
---|---|---|
10 | ACCOUNTING | NEW YORK |
20 | RESEARCH | DALLAS |
… | … | … |
工资等级表(salgrade
)
grade | losal | hisal |
---|---|---|
1 | 700 | 1200 |
2 | 1201 | 1400 |
… | … | … |
基本查询通常围绕单张表展开,比如筛选特定条件的记录、排序、分组统计等。例如:
- 查询工资高于500或岗位为
MANAGER
的员工,并且要求姓名首字母为大写J
:SELECT ename, sal, job FROM emp WHERE (sal > 500 OR job = 'MANAGER') AND ename LIKE 'J%';
- 按部门号升序、工资降序显示员工信息:
SELECT ename, deptno, sal FROM emp ORDER BY deptno ASC, sal DESC;
- 查询年薪最高的员工:
SELECT ename, sal*12 + IFNULL(comm, 0) AS annual_salary FROM emp ORDER BY annual_salary DESC LIMIT 1;
这些基础查询为后续的复合查询奠定了基础。接下来我们将深入探讨多表关联、自连接、子查询等高级查询技巧。
多表查询
为什么需要多表查询?
现实中的数据往往分散在多个表中。例如,员工信息存储在emp
表中,部门信息存储在dept
表中,而工资等级信息则在salsgrade
表中。如果需要查询某个员工的部门名称或工资等级,就必须将这些表关联起来。
笛卡尔积:多表查询的起点
多表查询的本质是对多张表取笛卡尔积,即所有记录的组合。例如:
SELECT * FROM emp, dept;
这条语句会返回emp
表和dept
表的笛卡尔积,结果中每一行都是emp
的一条记录与dept
的一条记录的组合。然而,这种组合大多是无意义的,因此需要通过WHERE
子句过滤出有效数据。
笛卡尔积的初步过滤
以查询部门号为10的员工信息为例:
SELECT d.dname, e.ename, e.sal
FROM emp e, dept d
WHERE e.deptno = d.deptno AND e.deptno = 10;
这里的关键是e.deptno = d.deptno
,它确保了只有员工所在部门与部门表中的记录匹配时才会被选中。
经典案例:查询员工的工资等级
要显示每个员工的姓名、工资和对应的工资等级,需要关联emp
和salsgrade
表:
SELECT e.ename, e.sal, s.grade
FROM emp e, salgrade s
WHERE e.sal BETWEEN s.losal AND s.hisal;
这里的BETWEEN
条件确保工资落在某个等级的范围内。
自连接
什么是自连接?
自连接是指同一张表与自身进行关联查询。例如,员工表中的mgr
字段表示上级领导的编号,而领导本身也是员工,因此可以通过自连接查询员工的领导信息。
案例:查询员工FORD的上级领导
SELECT e1.ename AS employee, e2.ename AS manager
FROM emp e1, emp e2
WHERE e1.mgr = e2.empno AND e1.ename = 'FORD';
这里通过给emp
表起两个别名e1
和e2
,分别代表员工和领导。e1.mgr = e2.empno
确保员工的领导编号与领导的员工编号匹配。
子查询
子查询是嵌套在其他SQL语句中的查询,常用于动态获取条件值。子查询可分为单行子查询、多行子查询、多列子查询等。
单行子查询
单行子查询返回单个值。例如,查询SMITH所在部门的其他员工:
SELECT ename, job, sal, deptno
FROM emp
WHERE deptno = (SELECT deptno FROM emp WHERE ename = 'SMITH') AND ename != 'SMITH';
子查询(SELECT deptno FROM emp WHERE ename = 'SMITH')
先获取SMITH的部门号,然后外层查询筛选出同一部门的其他员工。
多行子查询
多行子查询返回多行单列数据,常与IN
、ALL
、ANY
等关键字配合使用。
使用IN
筛选多行结果
查询与10号部门岗位相同的员工(不包括10号部门本身):
SELECT ename, job, sal, deptno
FROM emp
WHERE job IN (SELECT DISTINCT job FROM emp WHERE deptno = 10) AND deptno != 10;
子查询SELECT DISTINCT job FROM emp WHERE deptno = 10
先获取10号部门的所有岗位,外层查询通过IN
筛选出这些岗位的员工。
使用ALL
和ANY
比较多行结果
- 查询工资高于30号部门所有员工的员工:
SELECT ename, sal, deptno FROM emp WHERE sal > ALL(SELECT sal FROM emp WHERE deptno = 30);
- 查询工资高于30号部门任意员工的员工:
SELECT ename, sal, deptno FROM emp WHERE sal > ANY(SELECT sal FROM emp WHERE deptno = 30);
多列子查询
多列子查询返回多列数据,通常用于比较多个字段。例如,查询与SMITH部门和岗位完全相同的员工:
SELECT ename, deptno, job
FROM emp
WHERE (deptno, job) = (SELECT deptno, job FROM emp WHERE ename = 'SMITH') AND ename != 'SMITH';
在FROM子句中使用子查询
子查询不仅可以出现在WHERE
子句中,还可以作为临时表嵌套在FROM
子句中。例如,查询每个部门工资高于平均工资的员工:
SELECT e.ename, e.deptno, e.sal, tmp.avg_sal
FROM emp e,
(SELECT deptno, AVG(sal) AS avg_sal FROM emp GROUP BY deptno) tmp
WHERE e.deptno = tmp.deptno AND e.sal > tmp.avg_sal;
这里子查询tmp
先计算每个部门的平均工资,外层查询通过关联emp
和tmp
筛选出符合条件的员工。
合并查询
合并查询通过UNION
和UNION ALL
将多个查询结果合并。
UNION
与UNION ALL
的区别
UNION
:合并结果并去重。UNION ALL
:合并结果但不去重。
案例:查询工资大于2500或职位为MANAGER的员工
SELECT * FROM emp WHERE sal > 2500
UNION
SELECT * FROM emp WHERE job = 'MANAGER';
等价于:
SELECT * FROM emp WHERE sal > 2500 OR job = 'MANAGER';
但使用UNION
可以更清晰地拆分查询逻辑。
综合案例:查询每个部门的最高工资员工
方法一:子查询+多表查询
SELECT e.ename, e.deptno, e.sal, tmp.max_sal
FROM emp e,
(SELECT deptno, MAX(sal) AS max_sal FROM emp GROUP BY deptno) tmp
WHERE e.deptno = tmp.deptno AND e.sal = tmp.max_sal;
方法二:自连接
SELECT e1.ename, e1.deptno, e1.sal
FROM emp e1
WHERE e1.sal = (SELECT MAX(sal) FROM emp e2 WHERE e1.deptno = e2.deptno);
总结
MySQL的复合查询功能强大,能够处理复杂的业务需求。通过多表关联、自连接、子查询和合并查询,开发者可以灵活地组合数据,实现跨表分析、动态筛选和结果聚合。掌握这些技巧后,即使是面对海量数据,也能高效地提取所需信息。