文章目录
- MySQL
- 复合查询
- 1. 基本单表查询
- 2. 多表查询
- 3. 自连接
- 4. 子查询
- 4.1 单行子查询
- 4.2 多行子查询
- 4.3 多列子查询
- 4.4 使用from进行子查询
- 5. 合并查询
- 5.1 union
- 5.2 union all
MySQL
复合查询
数据库的复合查询是指在一个查询中结合使用多个查询条件或查询子句,以获取满足多个条件的记录。 这种查询方式在关系型数据库中非常常见,特别是在处理复杂的数据检索需求时。复合查询通常涉及一下操作:连接、子查询、聚合函数、分组、排序、筛选等。
1. 基本单表查询
MySQL的基本单表查询都是对一张表进行查询。
简单示例:我们创建一张表用于员工信息的存储:
其中包括员工的编号 id ,员工的姓名和工作,员工的薪资和入职日期,以及员工的部门编号和所对应的领导编号。其中编号 empno 作为我们的主键。
mysql> create table emp(
-> empno int unsigned primary key,
-> ename varchar(20),
-> job varchar(20),
-> salary int,
-> hiredate date,
-> deptno int,
-> );
忘了添加或者后续想要添加关于员工其他的列信息?我们可以使用 alter 进行对应表的修改以此来添加我们所需要的列信息。
mysql> alter table emp
-> add column mgr int;
查看表的结构:
desc emp;
插入信息:
mysql> insert into emp values(1001,'张三','总经理',10000,'2000-1-1',10,NULL);
mysql> insert into emp values(1002,'张花','员工',5000,'2005-7-4',10,1001);
mysql> insert into emp values(1003,'李四','经理',8000,'2002-3-12',20,NULL);
mysql> insert into emp values(1004,'李华','销售',8000,'2006-3-12',10,1001);
mysql> insert into emp values(1005,'王五','销售',7800,'2007-11-28',20,1002);
mysql> insert into emp values(1006,'赵六','销售',6700,'2008-5-18',10,1001);
mysql> insert into emp values(1007,'小美','员工',5000,'2010-2-08',20,1003);
mysql> insert into emp values(1008,'小帅','经理',9000,'2002-3-12',30,NULL);
mysql> insert into emp values(1009,'小蓝','员工',7600,'2007-5-22',30,NULL);
查看表的所有信息:
mysql> select *from emp;
查询工资高于6000且工作为 ‘员工’ 的雇员:
select * from emp where (salary>=6000 and job='员工');
按照部门号升序而雇员的工资降序排序:
select * from emp order by deptno ,salary desc;
显示工资最高的员工的名字、岗位和工资:
select ename,job,salary from emp where salary =(select max(salary) from emp);
使用年薪进行降序排序:
select ename ,salary*12 as '年薪' from emp order by 年薪 desc;
显示工资高于平均工资的员工信息:
select ename, salary from emp where salary>(select avg(salary) from emp);
显示每个部门的平均工资和最高工资:
select deptno, format(avg(salary), 2)as '平均工资' , max(salary)
from emp group by deptno;
显示每种岗位的雇员总数,平均工资:
select job,count(*)as'人员数量',
format(avg(salary),2)as'平均工资'
from emp
group by job;
2. 多表查询
实际开发中往往数据来自不同的表,所以需要多表查询。
为了实现多表查询,我们再创建一个员工部门表用于保存用户的部门名称和地点:
mysql> create table dept(
-> deptno int,
-> dname varchar(20),
-> loc varchar(20)
-> );
插入对应的员工部门编号和信息:
mysql> insert into dept values(10,'产品部','上海');
mysql> insert into dept values(20,'宣传部','北京');
mysql> insert into dept values(30,'技术部','深圳');
mysql> select * from dept;
显示各个员工的姓名,工资,工资和部门信息:
通常在多表查询的时候,我们会使用笛卡尔积将两张表格进行连接,然后使用where语句筛选出有效的信息。
显示各个员工的姓名,工资,工资和部门信息:
select ename,salary,emp.deptno,dname,loc
from emp,dept
where emp.deptno=dept.deptno;
显示部门号为10的部门名,员工名和工资:
select ename,salary,emp.deptno
from emp,dept
where emp.deptno=dept.deptno
and dept.det.deptno=10;
3. 自连接
自连接是指在同一张表连接查询。
显示员工张花的领导的编号和姓名:
方法一 使用的子查询:
我们先使用 select 语句查找到张花领导的 empno ,然后我们使用 select+where 查询后的结果作为条件,再次进行 select+where 查询输出领导的编号和姓名。
select empno,ename
from emp
where emp.empno=(select mgr from emp where ename='张花');
方法二 使用多表查询(自查询):
这里使用到表的别名,from emp leader, emp worker
,给自己的表起别名,将同一张表去两个名字(这样我们就会有两张表了,这两张表的内容完全一致,只是名字不同),同时因为要先做笛卡尔积,所以别名可以先识别。
select leader.empno,leader.ename
from emp leader, emp worker // 这里对表取别名,为leader worker
where leader.empno=worker.mgr
and worker.ename='张花';
4. 子查询
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询。这允许我们在一个查询中使用另一个查询的结果。子查询可以出现在 SELECT、FROM 或 WHERE 子句中,甚至在其他子查询中。子查询为 SQL 查询提供了极大的灵活性和复杂性,使你能够执行各种复杂的数据检索和计算任务。
4.1 单行子查询
返回一行记录的子查询。
同上,显示员工张花的领导的编号和姓名:
我们先使用 select 语句查找到张花领导的 empno ,然后我们使用 select+where 查询后的结果作为条件,再次进行 select+where 查询输出领导的编号和姓名。
select empno,ename
from emp
where emp.empno=(select mgr from emp where ename='张花');
4.2 多行子查询
返回多行记录的子查询。
查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号:
select ename,job,salary,deptno
from emp
where job
in (select job fromemp where deptno=10) ;
显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号:
select ename,salary,deptno
from emp
where salary > all(select salary from emp wheredeptno=30);
显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门
的员工):
select ename,salary,deptno
from emp
where salary >any(select salary from emp wheredeptno=30);
4.3 多列子查询
单行子查询是指子查询只返回单列,单行数据;多行子查询是指返回单列多行数据,都是针对单列而言的,而多列子查询则是指查询返回多个列数据的子查询语句。
查询和张花的岗位相同的所有雇员,且不含SMITH本人:
select ename
from emp
where(job)=(select job from emp where ename='张花 ')
and ename <> '张花';
4.4 使用from进行子查询
子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用。
显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资:
select ename,deptno,salary,format(asal,2)
from emp,
(select avg(salary) asal,deptno dt from emp group by deptno ) tmp
where emp.salary>tmp.asal and emp.deptno=tmp.dt;
子查询:
(select avg(sal) asal, deptno dt from EMP group by deptno) tmp
我们从EMP表中为每个deptno计算平均薪水(命名为asal)并将部门编号(命名为dt)一起选择出来。然后,这个子查询的结果集被命名为tmp。
主查询:
select ename, deptno, sal, format(asal,2) from EMP, tmp
主查询从EMP表和tmp子查询结果中选择ename(员工名字)、deptno(部门编号)、sal(薪水)以及格式化后的平均薪水asal。这里,format(asal,2)会将asal格式化为两位小数的形式。
连接条件:
where EMP.sal > tmp.asal and EMP.deptno=tmp.dt
这部分是连接条件,它指定了如何将EMP表与tmp子查询结果连接起来。具体来说,它要求:
EMP.sal(员工的薪水)必须大于tmp.asal(部门的平均薪水)。
EMP.deptno(员工的部门编号)必须等于tmp.dt(子查询中的部门编号)。
查找每个部门工资最高的人的姓名、工资、部门、最高工资:
select emp.ename,emp.salary ms
from emp,
(select max(salary) ms,deptno from emp group by deptno) tmp
where emp.deptno=tmp.deptno and emp.salary=tmp.ms;
子查询:
(select max(salary) ms, deptno from emp group by deptno) tmp
我们从emp表中为每个deptno(部门编号)选择最高的薪水(命名为ms)。子查询的结果集被命名为tmp。
主查询:
select emp.ename, emp.salary ms
from emp, tmp
主查询从emp表和tmp子查询结果中选择emp表中的ename(员工名字)和salary(薪水)。这里,emp.salary被重命名为ms,以与子查询中的ms列名保持一致。
连接条件:
where emp.deptno=tmp.deptno and emp.salary=tmp.ms
这部分是连接条件,它指定了如何将emp表与tmp子查询结果连接起来。具体来说,它要求:
emp.deptno(员工的部门编号)必须等于tmp.deptno(子查询中的部门编号)。
emp.salary(员工的薪水)必须等于tmp.ms(子查询中的该部门的最高薪水)。
显示每个部门的信息(部门名,编号,地址)和人员数量:
方法1 使用多表:
select dept.dname,dept.deptno,dept.loc,count(*) '部门人数'
from emp, dept
where emp.deptno=dept.deptno
group by dept.deptno,dept.dname,dept.loc;
选择的列:
select dept.dname, dept.deptno, dept.loc, count(*) '部门人数'
这部分选择了dept表中的dname(部门名称)、deptno(部门编号)和loc(部门位置)三列,同时还使用count( * )函数计算每个部门的员工数量,并将这个数量命名为’部门人数’。
数据来源:
from emp, dept
这里,查询从emp表和dept表中选择数据。由于使用了逗号分隔两个表,这是一个隐式连接(也称为笛卡尔积),这意味着它会返回emp表和dept表所有可能的行组合。
连接条件:
where emp.deptno=dept.deptno
这个条件确保了我们只连接那些emp表中的deptno与dept表中的deptno相匹配的记录。
分组:
group by dept.deptno, dept.dname, dept.loc
由于使用了count(*)聚合函数,我们需要通过GROUP BY子句来指定如何对结果进行分组。这里,我们按照dept表的deptno、dname和loc列进行分组,确保每个唯一的部门组合都会得到一个计数。
方法2 使用子查询:
select dept.deptno,dname,mycnt,loc
from dept,(select count(*) mycnt,deptno from emp group by deptno) tmp
where dept.deptno=tmp.deptno;
子查询:
(select count(*) mycnt, deptno from emp group by deptno) tmp
我们从emp表中为每个deptno(部门编号)计算员工数量(命名为mycnt)。子查询的结果集被命名为tmp。
主查询:
select dept.deptno, dname, mycnt, loc
from dept, tmp
主查询从dept表和tmp子查询结果中选择dept表的deptno(部门编号)、dname(部门名称)、mycnt(员工数量)以及loc(部门位置)。
连接条件:
where dept.deptno=tmp.deptno
这部分是连接条件,它指定了如何将dept表与tmp子查询结果连接起来。具体来说,它要求dept表中的deptno必须等于tmp子查询结果中的deptno。
5. 合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。合并查询通常指的是将两个或多个查询的结果组合在一起。在SQL中,你可以使用UNION或UNION ALL运算符来合并两个或多个SELECT语句的结果集。
5.1 union
union:该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
显示工资大于6000或职位是销售的雇员(去重):
select ename,salary,job
from emp
where salary>6000
union
select ename,salary,jobob
from emp
where job='销售';
5.2 union all
union all:该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
显示工资大于6000或职位是销售的雇员(不去重):
select ename,salary,job
from emp
where salary>6000
union all
select ename,salary,jobob
from emp
where job='销售';