Oracle高级查询语句
一、分组查询
1.分组函数group by :分组函数作用于一组数据,并对一组数据返回一个值
2.常用的分组函数:
·AVG 取平均值 ·SUM 求和 ·MIN 取最小值 ·MAX 取最大值 ·COUNT 统计次数
·WM_CONCAT 行转列 如:select deptno 部门号,wm_concat(ename) 部门中员工的姓名 from emp group by deptno;
3.在分组函数中使用nvl函数
·注意:nvl函数使分组函数无法忽略空值
4.group by 语句增强
select deptno,job,sum(sal) from emp group by deptno,job;
+
select deptno,sum(sal) from emp group by deptno;
+
select sum(sal) from emp;
=
select deptno,job,sum(sal) from emp group by rollup(deptno.job);
语法:
group by rollup(a,b)
等价于:
group by a,b
+
group by a
+
group by null
(用在报表里面效果很好)
5.sqlplus报表功能
加载报表设置文档 : @+文档path 后缀.sql
例:
ttitle col 15 '我的报表' col 35 sql.pno
col deptno heading 部门号
col job heading 职位
col sum(sal) heading 工资总额
break on deptno skip 1
二、多表查询
1.笛卡尔集:笛卡尔集是多表查询的基础
笛卡尔集中的列数等于每张表中的列数之和
笛卡尔集中的行数等于每张表中的行数之和
在实际运行环境下,应避免使用笛卡尔全集,应合理使用where连接条件(连接条件至少有表的总数(n) n-1个)
2.连接的类型
·等值连接
·不等值连接
select e.empno,e.ename,e.sal,s.grade
from emp e,salgrade s
where e.sal between s.losal and s.hisal;
·外链接
核心:通过外链接,把对于连接条件不成立的记录,仍然包含在最后的结果中
左外连接:当连接条件不成立的时候,等号左边的表仍然被包含
右外连接:当连接条件不成立的时候,等号右边的表仍然被包含
select d.deptno 部门号,d.dname 部门名称,count(e.empno) 人数
from emp e,dept d
where e.deptno(+)=d.deptno
group by d.deptno,d.dname
order by d.deptno;
·自连接
核心:通过别名,将同一张表视为多张表
select e.ename 员工姓名,b.ename 老板姓名
from emp e,emp b
where e.mgr=b.empno;
自连接存在的问题:
·不适合操作大表(笛卡尔集)
·解决办法:层次查询
select level,empno,ename,sal,mgr
from emp
connect by prior empno=mgr
start with mgr is null;
或者 start with empno=7839
(connect by 上一层的员工号=老板号)
三、子查询
1.子查询需要注意的十个问题:
·子查询语法中的小括号
·子查询的书写风格
·可以使用子查询的位置:where,select(后面的子查询必须为单行子查询),
having(例:select deptno,avg(sal)
from emp
group by deptno
having avg(sal)>(select max(sal)
from emp
where deptno=30);),from
·不可以使用子查询的位置:group by
·强调:from后面的子查询
·主查询和子查询可以不是同一张表
·一般不在子查询中使用排序,但在Top-N分析问题中,必须对子查询排序
select rownum,empno,ename,sal
from (select * from emp order by sal desc)
where rownum<=3;
(rownum 行号 伪列 rownum永远按照默认的顺序生成,只能使用<,<=;不能使用>,>=)
·一般先执行子查询,再执行主查询;但相关子查询例外
select empno,ename,sal,(select avg(sal) from emp where deptno=e.deptno) avgsal
from emp e
where sal>(select avg(sal) from emp where deptno=e.deptno);
·单行子查询只能使用单行操作符;多行子查询只能使用多行操作符
单行操作符:= < <= > >= <>
例1:select *
from emp
where job =(select job from emp where empno=7566)
and sal>(select sal from emp where empno=7782);
例2:select *
from emp
where sal=(select min(sal) from emp);
例3:select deptno,min(sal)
from emp
group by deptno
having min(sal)>(select min(sal)
from emp
where deptno=20);
多行操作符:IN(等于列表中的任何一个) ANY(和子查询返回的任意一个值比较) ALL(和子查询返回的所有值比较)
例:select *
from emp
where deptno in (select deptno from dept where dname = 'SALES' or dname = 'ACCOUNTING');
例:select *
from emp
where sal > any(select sal from emp where deptno=30);
例:select *
from emp
where sal > all(select sal from emp where deprno=30);
·注意:子查询中是null值的问题
例:select *
from emp
where empno in(select mgr from emp);
例:select *
from emp
where empno not in(select mgr from emp where mgr is not null);
案例一:分页查询显示员工信息:显示员工号,姓名,月薪
——每页显示四条记录
——显示第二页的员工
——按照月薪降序排列
select rownum,r,empno,ename,sal
from
(select rownum r,empno,ename,sal
from
(select rownum,empno,ename,sal from emp order by sal desc)
where rownum <=8)
where r>=5;
案例二:找到部门表中薪水大于部门平均薪水的员工
方法一:相关子查询
select empno,ename,sal,(select avg(sal) from emp where deptno=e.deptno) avgsal
from emp e
where sal > (select avg(sal) from emp where deptno=e.deptno);
方法二:多表查询
select e.empno,e.ename,e.sal,t.avgsal
from emp e,(select deptno,avg(sal) avgsal from emp group by deptno) t
where e.deptno=t.deptno and e.sal>t.avgsal;
对比:通过执行计划判断运行效率
explain plan for + 查询语句
select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 2385781174
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 43 | 8 (25)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
|* 2 | TABLE ACCESS FULL | EMP | 5 | 35 | 3 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 1 | 43 | 8 (25)| 00:00:01 |
| 4 | VIEW | VW_SQ_1 | 3 | 78 | 4 (25)| 00:00:01 |
| 5 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
我的报表 2
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
| 6 | TABLE ACCESS FULL| EMP | 14 | 98 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMP | 14 | 238 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 43 | 8 (25)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 43 | 8 (25)| 00:00:01 |
| 2 | VIEW | | 3 | 78 | 4 (25)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 238 | 3 (0)| 00:00:01 |
案例三:按部门统计每年的入职员工人数
方法一:
select count(*) total,
sum(decode(to_char(hiredate,'YYYY'),'1980',1,0)) "1980",
sum(decode(to_char(hiredate,'YYYY'),'1981',1,0)) "1981",
sum(decode(to_char(hiredate,'YYYY'),'1982',1,0)) "1982",
sum(decode(to_char(hiredate,'YYYY'),'1987',1,0)) "1987"
from emp;
方法二:
select
(select count(*) from emp) total,
(select count(*) from emp where to_char(hiredate,'yyyy')='1980') "1980",
(select count(*) from emp where to_char(hiredate,'yyyy')='1981') "1981",
(select count(*) from emp where to_char(hiredate,'yyyy')='1982') "1982",
(select count(*) from emp where to_char(hiredate,'yyyy')='1987') "1987"
from dual;