在前面的章节中,我们介绍的是单表查询,但是在本章节中我们要讲解的是多表查询。
设A,B为集合,用A中元素为第一元素,B中元素为第二元素构成有序对,所有这样的有序对组成的集合叫做A与B的笛卡尔积,记作AxB.
笛卡尔积的符号化为:
A x B = < x , y > ∣ x ∈ A ∧ y ∈ B AxB={
例如, A = a , b , B = 0 , 1 , 2 A={a,b},B={0,1,2} A=a,b,B=0,1,2,则
A x B = < a , 0 > , < a , 1 > , < a , 2 > , < b , 0 > , < b , 1 > , < b , 2 > AxB={,,,,,} AxB=<a,0>,<a,1>,<a,2>,<b,0>,<b,1>,<b,2>
B x A = < 0 , a > , < 0 , b > , < 1 , a > , < 1 , b > , < 2 , a > , < 2 , b > BxA={<0,a>,<0,b>,<1,a>,<1,b>,<2,a>,<2,b>} BxA=<0,a>,<0,b>,<1,a>,<1,b>,<2,a>,<2,b>
当我们涉及到多表查询的时候,MySQL会将各个表格以笛卡尔积的形式进行拼接,最终拼接为一张表。 所以表面上我们是对多个表进行查询,但实际上我们依旧是在进行单表查询。
比如我们现在有两个表A和B,那么mysql在拼接的时候,会将A表中每一条语句与B表中的所有语句进行拼接。也就是说,最终会罗列出记录拼接的所有可能。
当mysql进行拼接之后,所谓的多表查询就转化为了单表查询。
但是,由于mysql罗列出了所有的拼接可能,所以在这些拼接的结果中难免会有一些不符合逻辑的记录,此时就需要我们进行适当的筛选。
我们这里使用的是一个新的数据库:scott。
这个数据库中有三个表:dept、emp、salgrade
这三个表的描述如下:
因为上面的数据来自EMP和DEPT表,因此要联合查询。
我们先看看两个表各自的内容,再看一看两个表拼接后的内容。
emp表的内容如下:
dept的内容如下:
现在将两个表进行拼接,结果如下(由于拼接的结果很长,所以只展示一部分):
上面这个表中有一个缺点,我们发现,表中有两列deptno,但是某些记录在这两列对应的数值并不相等。这些不相等的数据就是我们要筛选掉的。
所以我们可以用如下语句进行筛选:
select * from emp, dept where emp.deptno = dept.deptno;
这里形如:表的名称.变量名
的语法格式非常像c中的结构体或者是c++中的类。
当我们将表中数据进行正确的筛选后,就可以进行单表查询了。
我们刚刚的需求是:显示雇员名、雇员工资以及所在部门的名字
所以可以使用下面的语句进行筛选:
select ename, sal,dname from emp, dept where emp.deptno = dept.deptno;
select dname,ename,sal from emp, dept where emp.deptno = dept.deptno and emp.deptno = 10;
select ename,sal,grade from emp, salgrade where emp.sal between salgrade.losal and salgrade.hisal;
自连接是指同一张表进行连接查询,即自己和自己做笛卡尔积运算。
显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号–empno)
select empno, ename from emp where emp.empno = (select mgr from emp where ename = 'FORD');
select leader.empno, leader.ename from emp leader, emp worker where worker.mgr = leader.empno and worker.ename = 'FORD';
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询。其实我们刚刚在介绍自连接的时候, 示例中所用到的第一个方法就是子查询。
显示SMITH同一部门的员工
select * from emp where deptno = (select deptno from emp where ename = 'SMITH');
多行子查询即:返回多行记录的子查询。
查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10自己的。
select ename, job, sal, deptno from emp where job in (select job from emp where deptno = 10) and deptno != 10;
显示工资比部门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的部门和岗位完全相同的所有雇员,不含SMITH本人。
select * from emp where (deptno, job) = (select deptno, job from emp where ename = 'SMITH') and ename != 'SMITH';
通过前面的例子,我们发现每次我们执行一个语句后,都会出现一个新的表。那么我们就可以把这个表当作一个临时表作为后续的查询需要。
我们先查询一下各个部门的平均工资。形式如下图所示:
现在我们将这个表作为新的表,并且给这个表起名为tmp。接下来就可以按照多表查询的方式去完成我们的题目了。
select ename, emp.deptno, sal, asal from emp,(select deptno, avg(sal) asal from emp group by deptno)o) tmp where emp.deptno = tmp.deptno and sal > asal;
select ename, sal, emp.deptno, msal from emp,(select deptno, max(sal) msal from emp group by deptno)) tmp where emp.deptno = tmp.deptno and emp.sal = msal;
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
select ename, sal, job from emp where sal > 2500 union select ename, sal, job from emp where job = 'MANAGER';
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
select ename, sal, job from emp where sal > 2500 union all select ename, sal, job from emp where job = 'MANAGER';