文章目录
1. 多表查询
基本概念
- 从多个表中获取数据

-
笛卡尔积,列数=两表列数之和,行数=两表行数之积

连接条件至少=表数量-1
2. 连接类型
2.1 等值连接
-
查询员工信息,要求显示:员工号,姓名,月薪,部门名称
SQL> SELECT e.EMPNO,e.ENAME,e.SAL,d.DNAME 2 FROM EMP e,DEPT d 3 WHERE e.DEPTNO=d.DEPTNO; EMPNO ENAME SAL DNAME ---------- -------------------- ---------- ---------------------------- 7369 SMITH 800 RESEARCH 7499 ALLEN 1600 SALES 7521 WARD 1250 SALES 7566 JONES 2975 RESEARCH 7654 MARTIN 1250 SALES 7698 BLAKE 2850 SALES 7782 CLARK 2450 ACCOUNTING 7839 KING 5000 ACCOUNTING 7844 TURNER 1500 SALES 7900 JAMES 950 SALES 7902 FORD 3000 RESEARCH 7934 MILLER 1300 ACCOUNTING 已选择 12 行。
不等值连接
-
查询员工信息,要求显示:员工号,姓名,月薪,薪水的级别
SQL> SELECT e.EMPNO,e.ENAME,e.SAL,s.GRADE 2 FROM emp e,SALGRADE s 3 WHERE e.SAL BETWEEN s.LOSAL and s.HISAL; EMPNO ENAME SAL GRADE ---------- -------------------- ---------- ---------- 7369 SMITH 800 1 7900 JAMES 950 1 7521 WARD 1250 2 7654 MARTIN 1250 2 7934 MILLER 1300 2 7499 ALLEN 1600 3 7844 TURNER 1500 3 7566 JONES 2975 4 7698 BLAKE 2850 4 7782 CLARK 2450 4 7902 FORD 3000 4 7839 KING 5000 5
外连接
核心:通过外连接,把对于连接条件不成立的记录,仍然包含在最后的结果中
左外链接:当连接条件不成立的时候,等号左边的表仍然被包含
右外链接:当连接条件不成立的时候,等号右边的表仍然被包含
-
按部门统计员工人数,要求显示:部门号,部门名称,人数
SQL> select d.deptno 部门号,d.dname 部门名称,count(e.empno) 人数 2 FROM emp e,dept d 3 where e.deptno=d.deptno 4 group by d.deptno,d.dname; 部门号 部门名称 人数 ---------- ---------------------------- ---------- 10 ACCOUNTING 3 20 RESEARCH 3 30 SALES SQL> select * from dept; DEPTNO DNAME LOC ---------- ---------------------------- -------------------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 6SQL> select * from dept; DEPTNO DNAME LOC ---------- ---------------------------- -------------------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON上面少了一条部门的记录,原因是什么?员工表中不存在40号部门的员工

-
右外连接
SQL> select d.deptno 部门号,d.dname 部门名称,count(e.empno) 人数 2 FROM emp e,dept d 3 where e.deptno(+)=d.deptno 4 group by d.deptno,d.dname 5 ; 部门号 部门名称 人数 ---------- ---------------------------- ---------- 30 SALES 6 10 ACCOUNTING 3 20 RESEARCH 3 40 OPERATIONS 0
自连接
-
查询员工姓名和员工的老板姓名
SQL> SELECT e.ENAME 员工姓名,b.ENAME 老板姓名 2 FROM EMP e,EMP b 3 where e.MGR=b.EMPNO 4 ; 员工姓名 老板姓名 -------------------- -------------------- FORD JONES ALLEN BLAKE WARD BLAKE MARTIN BLAKE TURNER BLAKE JAMES BLAKE MILLER CLARK JONES KING BLAKE KING CLARK KING SMITH FORD
自连接存在的问题,本质上还是多表查询
- 不适合操作大表
- 解决办法:层次查询
上层员工号=下层员工的老板号
SQL> select level, empno,ename,sal,mgr
2 from emp
3 connect by prior empno=mgr
4 start with mgr is null
5 ORDER BY 1;
LEVEL EMPNO ENAME SAL MGR
---------- ---------- -------------------- ---------- ----------
1 7839 KING 5000
2 7566 JONES 2975 7839
2 7698 BLAKE 2850 7839
2 7782 CLARK 2450 7839
3 7844 TURNER 1500 7698
3 7900 JAMES 950 7698
3 7654 MARTIN 1250 7698
3 7521 WARD 1250 7698
3 7934 MILLER 1300 7782
3 7902 FORD 3000 7566
3 7499 ALLEN 1600 7698
4 7369 SMITH 800 7902
- 层次查询:没有笛卡尔积,但是结果不够直观
- 多表查询:结果直观,但需要进行多表查询
3.子查询
3.1 什么时候使用子查询
3.2 子查询语法
- 查询工资比CLARK高的员工信息
SQL> select * from emp where sal>(select sal from emp where ename='CLARK');
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------
7566 JONES MANAGER 7839 02-4月 -81 2975 20
7698 BLAKE MANAGER 7839 01-5月 -81 2850 30
7839 KING PRESIDENT 17-11月-81 5000 10
7902 FORD ANALYST 7566 03-12月-81 3000 20
3.3 子查询需要注意的问题
-
子查询语法中的小括号
-
子查询的书写风格
-
可以使用子查询的位置: where, select, having,from
SQL> select deptno ,avg(sal) 2 from emp 3 group by deptno 4 having avg(sal) >(select max(sal) 5 from emp 6 where deptno=30); DEPTNO AVG(SAL) ---------- ---------- 10 2916.66667SQL> select ename 2 from (select empno,ename from emp); ENAME -------------------- SMITH ALLEN WARD JONES MARTIN BLAKE CLARK KING TURNER JAMES FORD MILLER -
不可以使用子查询的位置: group by
SQL> select avg(sal) from emp group by 2 (select deptno from emp); (select deptno from emp) * 第 2 行出现错误: ORA-22818: 这里不允许出现子查询表达式 -
强调:from后面的子查询可以看作成一张新的表
查询员工信息:员工号,姓名,月薪,年薪
SQL> select * 2 from (select empno, ename, sal, sal*12 annal from emp); EMPNO ENAME SAL ANNAL ---------- -------------------- ---------- ---------- 7369 SMITH 800 9600 7499 ALLEN 1600 19200 7521 WARD 1250 15000 7566 JONES 2975 35700 7654 MARTIN 1250 15000 7698 BLAKE 2850 34200 7782 CLARK 2450 29400 7839 KING 5000 60000 7844 TURNER 1500 18000 7900 JAMES 950 11400 7902 FORD 3000 36000 7934 MILLER 1300 15600 -
主查询和子查询可以不是同一张表
查询部门名称是 SALES的员工信息
SQL> select * 2 from emp 3 where deptno=(select deptno 4 from dept 5 where dname= 'SALES'); EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ---------- 7499 ALLEN SALESMAN 7698 20-2月 -81 1600 300 30 7521 WARD SALESMAN 7698 22-2月 -81 1250 500 7654 MARTIN SALESMAN 7698 28-9月 -81 1250 1400 7698 BLAKE MANAGER 7839 01-5月 -81 2850 7844 TURNER SALESMAN 7698 08-9月 -81 1500 0 7900 JAMES CLERK 7698 03-12月-81 950
-
一般不在子查询中,使用排序;但在Top-N分析问题中,必须对子查询排序
-
一般先执行子查询,再执行主查询;但相关子查询例外
-
eg:找到员工表中薪水大于本部门平均薪水的员工,可以使用相关子查询也可多表查询
select empno, ename, sal, from emp where sal >(select avg(sal) from emp where deptno=??)外面主查询员工所在的部门号,需要使用某种方式将员工部门号作为参数传到子查询中,需要使用别名
select empno, ename, sal,(select avg(sal) from emp where deptno=e.deptno) from emp e where sal >(select avg(sal) from emp where deptno=e.deptno) EMPNO ENAME SAL (SELECTAVG(SAL)FROMEMPWHEREDEPTNO=E.DEPTNO) ---------- -------------------- ---------- ------------------------------------------- 7499 ALLEN 1600 1566.66667 7698 BLAKE 2850 1566.66667 7839 KING 5000 2916.66667 7566 JONES 2975 2258.33333 7902 FORD 3000 2258.33333 -
单行孑查询只能使用单行操作符;多行孑查査询只能使用多行操作符
-
注意:子查询中是null值问题
3.4 单行子查询与多行子查询
概念
- 单行子查询返回一条记录,多行子查询返回多条记录
- select语句后的子查询必须是单行子查询
单行操作符
| 操作符 | 含义 |
|---|---|
| = | 等于 |
| > | 大于 |
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| <> | 不等于 |
多行操作符
| 操作符 | 含义 |
|---|---|
| in | 等于列表中的任何一个 |
| any | 和子查询返回的任意一个值比较 |
| all | 和子查询返回的所有值比较 |
示例
- 查询员工信息,要求-职位与7566员工一样,薪水大于7782员工的薪水
select *
from emp
where job=(select job from emp where empno=7566)and
sal >(select sal from emp where empno=7782);
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------
7566 JONES MANAGER 7839 02-4月 -81 2975 20
7698 BLAKE MANAGER 7839 01-5月 -81 2850 30
-
查询最低工资大于20号部门最低工资的部门号和部门的最低工资
select deptno, min(sal) from emp group by deptno having min(sal)>(select min(sal) from emp where deptno='20'); DEPTNO MIN(SAL) ---------- ---------- 30 950 10 1300查询部门名称是 SALES和 ACCOUNTING的员工信息
-
SQL> select * 2 from emp 3 where deptno in( select deptno from dept 4 where dname='SALES' or dname='ACCOUNTING'); EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ---------- 7499 ALLEN SALESMAN 7698 20-2月 -81 1600 300 30 7521 WARD SALESMAN 7698 22-2月 -81 1250 500 7654 MARTIN SALESMAN 7698 28-9月 -81 1250 1400 7698 BLAKE MANAGER 7839 01-5月 -81 2850 7782 CLARK MANAGER 7839 09-6月 -81 2450 10 7839 KING PRESIDENT 17-11月-81 5000 7844 TURNER SALESMAN 7698 08-9月 -81 1500 0 30 7900 JAMES CLERK 7698 03-12月-81 950 7934 MILLER CLERK 7782 23-1月 -82 1300 10 -
查询工资比30号部门任意一个员工高的员工信息
SQL> select * 2 from emp 3 where sal >any(select sal from emp where deptno=30);大于任意一个值,即大于最小值
3.5 rownum 行号
- 表中没有这一列,是Oracle设计用来帮助我们解决某些问题的
- 如果需要显示出来,需要显示的再查询语句加上rounum这一列,不写不会显示
- 行号永远按照默认的顺序生成
- 行号只能使用<,<=;不能使用>,>=
- 行号–Oracle临时表
SQL> select rownum, empno, ename, sal from emp order by sal desc;
ROWNUM EMPNO ENAME SAL
---------- ---------- -------------------- ----------
8 7839 KING 5000
11 7902 FORD 3000
4 7566 JONES 2975
6 7698 BLAKE 2850
7 7782 CLARK 2450
2 7499 ALLEN 1600
9 7844 TURNER 1500
12 7934 MILLER 1300
5 7654 MARTIN 1250
3 7521 WARD 1250
10 7900 JAMES 950
1 7369 SMITH 800
SQL> select rownum, empno, ename, sal
2 from (select * from emp order by sal desc)
3 where rownum<=3;
ROWNUM EMPNO ENAME SAL
---------- ---------- -------------------- ----------
1 7839 KING 5000
2 7902 FORD 3000
3 7566 JONES 2975
3.6 子查询的null值问题
- 单行子查询null问题
- 多行子查询null问题
- 使用not in 需要保证结果集中不含空置
查询不是老板的员工
SQL> select *
2 from emp
3 where empno not in (select mgr from emp);
未选定行

修改后的sql
SQL> select *
from emp
where empno not in (select mgr from emp where mgr is not null);
查询是老板的的员工
SQL> select *
2 from emp
3 where empno in (select mgr from emp);
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------
7902 FORD ANALYST 7566 03-12月-81 3000 20
7698 BLAKE MANAGER 7839 01-5月 -81 2850 30
7839 KING PRESIDENT 17-11月-81 5000 10
7566 JONES MANAGER 7839 02-4月 -81 2975 20
7782 CLARK MANAGER 7839 09-6月 -81 2450 10

4. 案例
4.1 分页查询
概念
- Oracle的分页查询是通过嵌套子查询来实现的
- MySQL的分页查询是通过limit关键字来实现的
示例
分页查询显示员工信息:显示员工号,姓名,月薪
- 每页显示四条记录
- 显示第二页的员工
- 按照月薪降序排列
- 错误示例(Oracle数据库是一个行式数据库,取了第一行才能取第二行,行号只能从1开始)
SQL> select rownum, empno, ename, sal from emp
2 ;
ROWNUM EMPNO ENAME SAL
---------- ---------- -------------------- ----------
1 7369 SMITH 800
2 7499 ALLEN 1600
3 7521 WARD 1250
4 7566 JONES 2975
5 7654 MARTIN 1250
6 7698 BLAKE 2850
7 7782 CLARK 2450
8 7839 KING 5000
9 7844 TURNER 1500
10 7900 JAMES 950
11 7902 FORD 3000
12 7934 MILLER 1300
SQL> select rownum, empno, ename, sal from emp
2 where rownum>=5 and rownum <=8;
未选定行
正确示例
select r,empno,ename,sal
from (select rownum r, empno, ename, sal
from (select rownum, empno, ename, sal from emp order by sal desc)el
where rownum<=8) e2
where r>=5;
R EMPNO ENAME SAL
---------- ---------- -------------------- ----------
5 7782 CLARK 2450
6 7499 ALLEN 1600
7 7844 TURNER 1500
8 7934 MILLER 1300
找到员工表中薪水大于本部门平均薪水的员工。
- 关联子查询方式
select empno, ename, sal,(select avg(sal) from emp where deptno=e.deptno)
from emp e
where sal >(select avg(sal) from emp where deptno=e.deptno)
- 多表、嵌套查询方式
select e.empno, e.ename,e.sal, d.avgsal
from emp e,(select deptno, avg(sal) avgsal from emp group by deptno) d
where e.deptno=d.deptno and e.sal>d.avgsal;
EMPNO ENAME SAL AVGSAL
---------- -------------------- ---------- ----------
7499 ALLEN 1600 1566.66667
7566 JONES 2975 2258.33333
7698 BLAKE 2850 1566.66667
7839 KING 5000 2916.66667
7902 FORD 3000 2258.33333
按部门统计员工人数
SQL> select count (*) Total,
2 sum(decode(to_char (hiredate, 'YYYY'),'1980',1, 0)) "1980",
3 sum(decode(to_char (hiredate, 'YYYY'),'1981',1, 0)) "1981",
4 sum(decode(to_char (hiredate, 'YYYY'),'1982',1, 0)) "1982",
5 sum(decode(to_char (hiredate, 'YYYY'),'1987',1, 0)) "1987"
6 from emp;
TOTAL 1980 1981 1982 1987
---------- ---------- ---------- ---------- ----------
12 1 10 1 0
子查询

查看sql执行效率
- explain plan for
- select * from table(dbms_xplan.display);
SQL> explain plan for select e.empno, e.ename,e.sal, d.avgsal
2 from emp e,(select deptno, avg(sal) avgsal from emp group by deptno) d
3 where e.deptno=d.deptno and e.sal>d.avgsal;
已解释。
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 43 | 5 (20)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 43 | 5 (20)| 00:00:01 |
| 2 | VIEW | | 3 | 78 | 3 (34)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 12 | 84 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 12 | 204 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E"."DEPTNO"="D"."DEPTNO")
filter("E"."SAL">"D"."AVGSAL")
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2399348195
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 69 | 8 (25)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 1 | 69 | 8 (25)| 00:00:01 |
|* 2 | HASH JOIN | | 1 | 43 | 5 (20)| 00:00:01 |
| 3 | VIEW | VW_SQ_1 | 3 | 78 | 3 (34)| 00:00:01 |
| 4 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 5 | TABLE ACCESS FULL| EMP | 12 | 84 | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | EMP | 12 | 204 | 2 (0)| 00:00:01 |
| 7 | VIEW | VW_SSQ_2 | 3 | 78 | 3 (34)| 00:00:01 |
| 8 | HASH GROUP BY | | 3 | 21 | 3 (34)| 00:00:01 |
| 9 | TABLE ACCESS FULL | EMP | 12 | 84 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ITEM_2"(+)="E"."DEPTNO")
2 - access("ITEM_1"="E"."DEPTNO")
filter("SAL">"AVG(SAL)")
已选择 23 行。
9185



被折叠的 条评论
为什么被折叠?



