多表查询SQL

目录

一、建表语句

二、多表连接查询

2.1 内连接查询

2.2 外连接查询

2.3 三表连接查询

2.4 自连接查询

三、子查询

3.1 不相关子查询

3.2 相关子查询

四、综合练习


一、建表语句

员工表

CREATE TABLE `emp` (
  `empno` int(11),
  `ename` varchar(255) DEFAULT NULL,
  `job` varchar(255) DEFAULT NULL,
  `mgr` varchar(255) DEFAULT NULL,
  `hiredate` date DEFAULT NULL,
  `sal` int(11) DEFAULT NULL,
  `comm` int(11) DEFAULT NULL,
  `deptno` int(11) DEFAULT NULL,
  PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7369', 'SMITH', 'CLERK', '7902', '1980-12-17', '800', NULL, '20');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7499', 'ALLEN', 'SALESMAN', '7698', '1981-02-20', '1600', '300', '30');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7521', 'WARD', 'SALESMAN', '7698', '1981-02-22', '1250', '500', '30');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7566', 'JONES', 'MANAGER', '7839', '1981-04-02', '2975', NULL, '20');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7654', 'MARTIN', 'SALESMAN', '7698', '1981-09-28', '1250', '1400', '30');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7698', 'BLAKE', 'MANAGER', '7839', '1981-05-01', '2850', NULL, '30');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7782', 'CLARK', 'MANAGER', '7839', '1981-06-09', '2450', NULL, '10');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7788', 'SCOTT', 'ANALYST', '7566', '1987-04-19', '3000', NULL, NULL);
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7839', 'KING', 'PRESIDENT', NULL, '1981-11-17', '5000', NULL, '10');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7934', 'MILLER', 'CLERK', '7782', '1982-01-23', '1300', NULL, '10');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7876', 'ADAMS', 'CLERK', '7788', '1987-05-23', '1100', NULL, '20');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7902', 'FORD', 'ANALYST', '7566', '1981-12-03', '3000', NULL, '20');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7844', 'TURNER', 'SALESMAN', '7698', '1981-09-08', '1500', NULL, '30');
INSERT INTO `test`.`emp` (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) VALUES ('7900', 'JAMES', 'CLERK', '7698', '1981-12-03', '950', NULL, '30');

部门表

CREATE TABLE `dept` (
  `deptno` int(11),
  `dname` varchar(255) DEFAULT NULL,
  `loc` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test`.`dept` (`deptno`, `dname`, `loc`) VALUES ('10', 'ACCOUNTING', 'NEW YOUK');
INSERT INTO `test`.`dept` (`deptno`, `dname`, `loc`) VALUES ('20', 'RESEARCH', 'DALLAS');
INSERT INTO `test`.`dept` (`deptno`, `dname`, `loc`) VALUES ('30', 'SALES', 'CHICAGO');
INSERT INTO `test`.`dept` (`deptno`, `dname`, `loc`) VALUES ('40', 'OPERATIONS', 'BOSTON');

薪资等级表

CREATE TABLE `salgrade` (
  `grade` int(11),
  `losal` int(11) DEFAULT NULL,
  `hisal` int(11) DEFAULT NULL,
  PRIMARY KEY (`grade`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test`.`salgrade` (`grade`, `losal`, `hisal`) VALUES ('1', '700', '1200');
INSERT INTO `test`.`salgrade` (`grade`, `losal`, `hisal`) VALUES ('2', '1201', '1400');
INSERT INTO `test`.`salgrade` (`grade`, `losal`, `hisal`) VALUES ('3', '1401', '2000');
INSERT INTO `test`.`salgrade` (`grade`, `losal`, `hisal`) VALUES ('4', '2001', '3000');
INSERT INTO `test`.`salgrade` (`grade`, `losal`, `hisal`) VALUES ('5', '3001', '9999');

二、多表连接查询

2.1 内连接查询

多表查询:查询员工的员工编号、姓名、薪水和部门编号、部门名称

交叉连接:笛卡尔积,没有实际的意义,有理论的意义,让我们明白连接查询底层的原理

SELECT * FROM emp CROSS JOIN dept

自然连接:会自动的匹配所有的同名列(目前同名列只有deptno),并且同名列只保留一份
最大的有点:简单

SELECT * FROM emp natural JOIN dept
SELECT empno,ename,sal,deptno,dname FROM emp natural JOIN dept

缺点:没有指明各列属于哪个表,效率地下
解决:指定表明

SELECT emp.empno,emp.ename,emp.sal,emp.deptno,dept.dname FROM emp natural JOIN dept

缺点:表明可能比较长
解决:起别名

SELECT e.empno,e.ename,e.sal,e.deptno,d.deptno,d.dname FROM emp e natural JOIN dept d

using子句

natural自然连接的缺点:会自动的按照所有的同名列进行匹配,如果希望只按照某个同名列进行匹配,怎么办?
解决:using子句,注意natural就不要了

SELECT e.empno,e.ename,e.sal,e.deptno,d.deptno,d.dname FROM emp e JOIN dept d USING (deptno)

on子句
natural、using的缺点:必须有同名列,如果有主键、外键不同名,怎么办?
解决:on子句

SELECT e.empno,e.ename,e.sal,e.deptno,d.deptno,d.dname FROM emp e JOIN dept d ON e.deptno = d.deptno

问题:到底使用哪种连接查询

  1. cross  不用
  2. natural、using:必须有同名列
  3. on 不管是否有同名列,均可使用,可读性强
  4. 建议使用on子句

内连接查询带筛选条件

SELECT
	e.empno,
	e.ename,
	e.sal,
	e.deptno,
	d.deptno,
	d.dname
FROM
	emp e
JOIN dept d ON e.deptno = d.deptno
WHERE sal > 2000 
ORDER BY sal DESC

内连接查询:只显示匹配的数据,有些员工没有部门,这些员工不会被匹配

2.2 外连接查询

外连接查询:除了显示匹配的数据,还可以匹配全部或者部分不匹配的数据

左外连接 left join:除了显示匹配的数据,还可以显示左表的不匹配数据

SELECT * FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno

右外连接 right join:除了显示匹配的数据,还可以显示右表的不匹配数据

SELECT * FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno

全外连接 full outer join:除了显示匹配的数据,还可以显示左、右表的不匹配数据
MySQL还不支持全外连接,Oracle支持
变通的解决方案

SELECT * FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno
UNION
SELECT * FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno

2.3 三表连接查询

多表查询:查询员工的员工编号、姓名、薪水和部门编号、部门名称、薪水等级

第一步:查询员工的员工编号、姓名、薪水和部门编号、部门名称

为什么使用左外连接?SCOTT虽然没有部门,但是他有薪水

SELECT
	e.empno,
	e.ename,
	e.sal,
	e.deptno,
	d.deptno,
	d.dname
FROM
	emp e
LEFT JOIN dept d ON e.deptno = d.deptno

第二步:查询员工的员工编号、姓名、薪水和部门编号、部门名称、薪水等级

SELECT
	e.empno,
	e.ename,
	e.sal,
	e.deptno,
	d.deptno,
	d.dname,
	sg.*
FROM
	emp e
LEFT JOIN dept d ON e.deptno = d.deptno 
JOIN salgrade sg ON e.sal BETWEEN sg.losal AND sg.hisal

2.4 自连接查询

查询员工的编号、姓名、上级编号、上级姓名

第一步:查询员工的编号、姓名、上级编号

SELECT empno,ename,mgr FROM emp

第二步:查询员工的编号、姓名、上级编号、上级姓名

SELECT
	e.empno,
	e.ename,
	e.mgr,
	m.empno,
	m.ename
FROM
	emp e    -- 员工表
LEFT JOIN emp m     -- 上级表
ON e.mgr = m.empno

三、子查询

子查询:查询的嵌套 外查询 子查询
不相关子查询:子查询可以独立运行;先运行子查询,在运行外查询
相关子查询:子查询不可以独立运行;先运行外查询,在运行子查询

3.1 不相关子查询

单行子查询:直接比较即可 < > =

查询所有比CLARK工资高的员工的信息

SELECT * FROM emp WHERE sal > (SELECT sal FROM emp WHERE ename = 'ClARK')

查询工资高于平均工资的雇员名字和工资

SELECT * FROM emp WHERE sal > (SELECT avg(sal) FROM emp)

查询和ClARK同一部门且比他工资高的雇员名字和工资

SELECT ename,sal FROM emp WHERE job = (SELECT job FROM emp WHERE ename = 'ClARK') AND sal > (SELECT sal FROM emp WHERE ename = 'ClARK')

查询职务和SCOTT相同,比SCOTT雇佣时间早的雇员信息

SELECT * FROM emp WHERE job = (SELECT job FROM emp WHERE ename = 'SCOTT') AND hiredate < (SELECT hiredate FROM emp WHERE ename = 'SCOTT')

多行子查询:不可以直接比较,需要借助关键字 in any all

查询工资低于任意一个职务为CLERK的员工工资的雇员信息

SELECT * FROM emp WHERE sal < ANY (SELECT sal FROM emp WHERE job = 'CLERK') AND job != 'CLERK'

查询工资比SALESMAN部门所有的雇员都高,雇员名字和工资

SELECT empno,ename,sal FROM emp WHERE sal > (SELECT MAX(sal) FROM emp WHERE job = 'SALESMAN')
SELECT empno,ename,sal FROM emp WHERE sal > ALL (SELECT sal FROM emp WHERE job = 'SALESMAN')

查询部门20中职务同部门10的雇员职务一样的雇员信息

SELECT * FROM emp WHERE deptno = 20 AND job in (SELECT job FROM emp WHERE deptno = 10)
SELECT * FROM emp WHERE deptno = 20 AND job = ANY (SELECT job FROM emp WHERE deptno = 10)

3.2 相关子查询

相关子查询:子查询不可以独立运行;先运行外查询,在运行子查询
好处:简单、功能强大(一些使用不相关子查询不能实现或者实现繁琐的子查询,可以使用相关子查询实现)

查询最高工资的员工(不相关子查询)

SELECT * FROM emp WHERE sal = (SELECT max(sal) FROM emp)

查询本部门最高工资的员工(相关子查询)

SELECT * FROM emp e WHERE sal = (SELECT max(sal) FROM emp WHERE deptno = e.deptno)

查询工资高于其所在部门的平均工资的员工信息(相关子查询)

SELECT * FROM emp e WHERE sal > (SELECT avg(sal) FROM emp WHERE deptno = e.deptno)

四、综合练习

查询每个部门平均薪水的等级(连接查询,子查询,group by)

SELECT
	das.*,sg.grade
FROM
	(
		SELECT
			deptno,
			avg(sal) asl
		FROM
			emp
		GROUP BY
			deptno
	) das
JOIN salgrade sg ON das.asl BETWEEN sg.losal AND sg.hisal

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值