在实际开发中,仅用单表查询显然无法满足复杂业务需求。今天我们就以经典的员工管理系统(emp员工表、dept部门表、salgrade工资级别表)为例,聊聊 mysql 复合查询的核心玩法 —— 从单表查询到多表联查、子查询,再到合并查询,每一步都附上真实查询结果,帮你直观理解。
一、先回顾:单表查询的基础操作(以emp表为例)
先明确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 |
| 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 |
1. 带条件的筛选
select * from emp where (sal>500 or job='manager') and ename like 'j%';
查询结果:
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
|---|---|---|---|---|---|---|---|
| 7566 | jones | manager | 7839 | 1981-04-02 | 2975 | null | 20 |
| 7900 | james | clerk | 7698 | 1981-12-03 | 950 | null | 30 |
2. 排序与计算字段
(1)按部门号升序、工资降序排列
select * from emp order by deptno, sal desc;
查询结果(节选核心字段):
| empno | ename | sal | deptno |
|---|---|---|---|
| 7839 | king | 5000 | 10 |
| 7782 | clark | 2450 | 10 |
| 7934 | miller | 1300 | 10 |
| 7788 | scott | 3000 | 20 |
| 7902 | ford | 3000 | 20 |
| 7566 | jones | 2975 | 20 |
| 7876 | adams | 1100 | 20 |
| 7369 | smith | 800 | 20 |
| 7698 | blake | 2850 | 30 |
| 7499 | allen | 1600 | 30 |
| 7844 | turner | 1500 | 30 |
| 7521 | ward | 1250 | 30 |
| 7654 | martin | 1250 | 30 |
| 7900 | james | 950 | 30 |
(2)计算年薪(工资 ×12 + 奖金,奖金为空则按 0 算)并排序
select ename, sal*12+ifnull(comm,0) as '年薪' from emp order by 年薪 desc;
查询结果:
| ename | 年薪 |
|---|---|
| king | 60000 |
| scott | 36000 |
| ford | 36000 |
| jones | 35700 |
| blake | 34200 |
| clark | 29400 |
| allen | 19500 |
| turner | 18000 |
| martin | 16400 |
| miller | 15600 |
| ward | 15500 |
| adams | 13200 |
| james | 11400 |
| smith | 9600 |
3. 聚合与分组查询
(1)查每个部门的平均工资、最高工资
select deptno, format(avg(sal),2) as avg_sal, max(sal) as max_sal from emp group by deptno;
查询结果:
| deptno | avg_sal | max_sal |
|---|---|---|
| 10 | 2,916.67 | 5000 |
| 20 | 2,175.00 | 3000 |
| 30 | 1,566.67 | 2850 |
(2)筛选平均工资 < 2000的部门
select deptno, avg(sal) as avg_sal from emp group by deptno having avg_sal<2000;
查询结果:
| deptno | avg_sal |
|---|---|
| 30 | 1566.6667 |
二、多表查询:跨表关联数据
先明确关联表基础数据:
dept部门表
| deptno | dname | loc |
|---|---|---|
| 10 | accounting | new york |
| 20 | research | dallas |
| 30 | sales | chicago |
| 40 | operations | boston |
salgrade工资级别表
| grade | losal | hisal |
|---|---|---|
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
1. 基础多表联查(等值连接)
(1)查 “员工名、工资、所属部门名”
select emp.ename, emp.sal, dept.dname from emp, dept where emp.deptno = dept.deptno;
查询结果(节选):
| ename | sal | dname |
|---|---|---|
| smith | 800 | research |
| allen | 1600 | sales |
| ward | 1250 | sales |
| jones | 2975 | research |
| martin | 1250 | sales |
| blake | 2850 | sales |
| clark | 2450 | accounting |
| scott | 3000 | research |
| king | 5000 | accounting |
| turner | 1500 | sales |
| adams | 1100 | research |
| james | 950 | sales |
| ford | 3000 | research |
| miller | 1300 | accounting |
(2)限定部门号为 10的员工
select ename, sal, dname from emp, dept where emp.deptno=dept.deptno and dept.deptno=10;
查询结果:
| ename | sal | dname |
|---|---|---|
| clark | 2450 | accounting |
| king | 5000 | accounting |
| miller | 1300 | accounting |
2. 三表联查(含工资级别表)
select ename, sal, grade from emp, salgrade where emp.sal between losal and hisal;
查询结果:
| ename | sal | grade |
|---|---|---|
| smith | 800 | 1 |
| allen | 1600 | 3 |
| ward | 1250 | 2 |
| jones | 2975 | 4 |
| martin | 1250 | 2 |
| blake | 2850 | 4 |
| clark | 2450 | 4 |
| scott | 3000 | 4 |
| king | 5000 | 5 |
| turner | 1500 | 3 |
| adams | 1100 | 1 |
| james | 950 | 1 |
| ford | 3000 | 4 |
| miller | 1300 | 2 |
三、自连接:同一张表查上下级
-- 别名leader代表领导,worker代表员工 select leader.empno, leader.ename from emp leader, emp worker where leader.empno = worker.mgr and worker.ename='ford';
查询结果:
| empno | ename |
|---|---|
| 7566 | jones |
四、子查询:用查询结果当条件 / 临时表
1. 单行子查询(返回一条结果)
select ename, job from emp where sal = (select max(sal) from emp);
查询结果:
| ename | job |
|---|---|
| king | president |
2. 多行子查询(返回多条结果)
(1)查 “和 10 号部门岗位相同、但不属于 10 号部门” 的员工
select ename,job,sal,deptno from emp where job in (select distinct job from emp where deptno=10) and deptno<>10;
查询结果:
| ename | job | sal | deptno |
|---|---|---|---|
| jones | manager | 2975 | 20 |
| blake | manager | 2850 | 30 |
| smith | clerk | 800 | 20 |
| adams | clerk | 1100 | 20 |
| james | clerk | 950 | 30 |
(2)查 “工资比 30 号部门所有员工都高” 的员工
select ename, sal, deptno from emp where sal > all(select sal from emp where deptno=30);
查询结果:
| ename | sal | deptno |
|---|---|---|
| jones | 2975 | 20 |
| scott | 3000 | 20 |
| king | 5000 | 10 |
| ford | 3000 | 20 |
3. from 子句子查询(把子查询当临时表)
-- 先查各部门平均工资(临时表tmp),再关联员工表 select ename, deptno, sal, format(tmp.avg_sal,2) as dept_avg_sal from emp, (select avg(sal) avg_sal, deptno dt from emp group by deptno) tmp where emp.sal > tmp.avg_sal and emp.deptno=tmp.dt;
查询结果:
| ename | deptno | sal | dept_avg_sal |
|---|---|---|---|
| king | 10 | 5000 | 2,916.67 |
| jones | 20 | 2975 | 2,175.00 |
| scott | 20 | 3000 | 2,175.00 |
| ford | 20 | 3000 | 2,175.00 |
| blake | 30 | 2850 | 1,566.67 |
| allen | 30 | 1600 | 1,566.67 |
五、合并查询:union/union all
1. union(自动去重)
select ename, sal, job from emp where sal>2500 union select ename, sal, job from emp where job='manager';
查询结果(无重复数据):
| ename | sal | job |
|---|---|---|
| jones | 2975 | manager |
| blake | 2850 | manager |
| scott | 3000 | analyst |
| king | 5000 | president |
| ford | 3000 | analyst |
| clark | 2450 | manager |
2. union all(保留重复)
select ename, sal, job from emp where sal>2500 union all select ename, sal, job from emp where job='manager';
查询结果(jones、blake 重复出现):
| ename | sal | job |
|---|---|---|
| jones | 2975 | manager |
| blake | 2850 | manager |
| scott | 3000 | analyst |
| king | 5000 | president |
| ford | 3000 | analyst |
| jones | 2975 | manager |
| blake | 2850 | manager |
| clark | 2450 | manager |
总结
mysql 复合查询是实际开发的核心技能,核心是理清表关系、灵活组合单表 / 多表 / 子查询语法。
本文所有示例均基于真实员工管理表数据,查询结果可直接验证,建议你复制 sql 语句在本地数据库中实操,更快掌握各类查询技巧~
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论