高级查询
1.关联查询
作用:可以跨多表查询
--查询出员工的名字和他所在部门的名字
//错误
//select first_name,name from s_emp,s_dept;
//错误原因:
产生了笛卡尔积(两张表中的数据每条记录都进行匹配),
导致查询结果中出现了很多冗余的结果,所以,为了避免
笛卡尔积,我们应该采用where字句对查询结果进行过滤.
//正确
select first_name,name from s_emp,s_dept
where s_dept.id = s_emp.dept_id;
Oracle中可以给列取别名,还可以给表取别名,甚至可以给查询结果取别名,
可以把上述语句改造成:
//传统写法:
select e.first_name,d.name from s_emp e,s_dept d
where e.dept_id = d.id;
//新写法:
select e.first_name,d.name from s_emp e
join s_dept d on e.dept_id = d.id;
练习:
--查询出部门名以及它所在的区域名称
select d.name 部门名,r.name 区域名 from s_dept d join s_region r
on d.region_id=r.id;
--查询出'Sales'部门的所有员工的名字和工资
select e.first_name,e.salary,d.name from s_emp e join s_dept d
on e.dept_id=d.id where d.name='Sales';
--查询出设在Asia的部门名
select d.name,r.name from s_dept d join s_region r
on d.region_id=r.id where r.name='Asia';
--查询出名字叫Unisports的客户订单的信息
select c.name,o.* from s_customer c join s_ord o
on o.customer_id=c.id where c.name='Unisports';
--查询出设在Asia工作的员工名,工资,职称
select e.first_name,e.salary,e.title,r.name from s_emp e
join s_dept d on e.dept_id=d.id
join s_region r on d.region_id=r.id
where r.name='Asia';
--查询出客户名及它的订单号,总费用
select c.name,o.id,o.total from s_customer c left join s_ord o
on o.customer_id=c.id;
--查询订单号,订单费用以及该订单所对应的客户名
select o.id,o.total,c.name from s_ord o left join s_customer c
on o.customer_id=c.id;
select o.id,o.total,c.name from s_customer c right join s_ord o
on o.customer_id=c.id;
我们的关联查询分为2大类:
1.内联查询
[inner] join
2.外联查询
a.左外联:以关联点(join)左边的表为基准,非基准表中没有数据与
基准表中匹配,则基准表中的行记录还是会被显示.
left [outer] join
b.右外联:以关联点(join)右边的表为基准,非基准表中没有数据与
基准表中匹配,则基准表中的行记录还是会被显示.
right [outer] join
注意:A left join B == B right join A
c.全外联
full [outer] join
d.交叉联:返回连接两张表格的笛卡尔积
cross [outer] join
自关联查询:同一张表自己关联自己
--找出员工名以及他的上司名
select e.first_name 员工名,m.first_name 上司名 from s_emp e join s_emp m
on e.manager_id=m.id;
练习:
--查询出在'Asia'工作的员工
select e.*,r.name from s_emp e
join s_dept d on e.dept_id=d.id
join s_region r on d.region_id=r.id
where r.name='Asia';
--找出Womansport所购买的订单信息(订单编号,费用,支付方式)
select c.name,o.id,o.total,o.payment_type from s_customer c
left join s_ord o on o.customer_id=c.id where c.name='Womansport';
--找出Operations部门工作的员工名,工资,并且按照工资降序排列
select e.first_name,e.salary,d.name from s_emp e
join s_dept d on e.dept_id=d.id
where d.name='Operations'
order by 2 desc;
----------------------------------------------------------
2.分组查询
定义:利用内置的分组函数来查询
Oracle数据库中提供了相对应的分组函数来支持分组查询:
sum() 求总和,会自动忽略null值
count() 求总个数,包含null值和相同值
avg() 求平均数,会自动忽略null值
max() 求最大值
min() 求最小值
注意:组函数也叫做多行函数,与单行函数不同的是,组函数可以
同时处理多行数据.
分组的语法:
select 列名 [列别名],组函数(列名) from 表名
where 字句
group by 列名
having 字句
order by 列;
--查询出全公司的最高工资,最低工资,平均工资和工资总和
select max(salary),min(salary),avg(salary),sum(salary) from s_emp;
--查询出各个部门的最高工资,最低工资,平均工资和工资总和
select dept_id,max(salary),min(salary),avg(salary),sum(salary) from s_emp
group by dept_id;
--查询出41,42,50部门的最高工资,最低工资,平均工资和工资总和
select dept_id,max(salary),min(salary),avg(salary),sum(salary) from s_emp
where dept_id in(41,42,50) group by dept_id;
where和having的差别
1.where是条件过滤,是在分组之前的过滤,不能使用组函数.
2.having是在分组之后的进一步过滤,可以使用组函数.
注意:
出现在group by后面的列,才能出现select后面,除非它被组函数修饰!!!
练习:
--查询出各个区域名和设在此区域的部门数量
select r.id,r.name,count(d.id) from s_region r left join s_dept d
on d.region_id=r.id group by r.id,r.name;
--查询出客户名及客户的订单数
select c.name,count(o.id) from s_customer c left join s_ord o
on o.customer_id=c.id group by c.name;
--查询出订单数超过一个的客户
select c.id,c.name,c.phone,count(o.id) from s_customer c left join s_ord o
on o.customer_id=c.id group by c.id,c.name,c.phone having count(o.id)>1;
--查询出平均工资超过1300的部门编号
select dept_id,avg(salary) from s_emp group by dept_id
having avg(salary)>1300;
--统计本公司的员工数
select count(*) from s_emp;
--统计本公司的职称个数
select count(*) from s_title;
select count(distinct title) from s_emp;
--统计各个部门的员工数,按照员工数降序排列
select e.dept_id,count(*) from s_emp e group by e.dept_id order by 2 desc;
select d.id,d.name,count(*) from s_emp e join s_dept d
on e.dept_id=d.id group by d.id,d.name order by 3 desc;
--查询出各个区域及本区域工作的员工数,并且按照员工数降序排列
select r.id,r.name,count(*) from s_emp e
join s_dept d on e.dept_id=d.id
join s_region r on d.region_id=r.id
group by r.id,r.name
order by 3 desc;
--查询出各职称的员工数量
select title,count(*) from s_emp group by title;
--查询出工资超过1200的各部门员工数量
select dept_id,count(*) from s_emp where salary>1200 group by dept_id;
--查询出人数超过3个员工的部门编号和部门名称
select d.id,d.name,count(*) from s_dept d join s_emp e
on e.dept_id=d.id group by d.id,d.name having count(*)>3;
-----------------------------------------------------------
3.子查询
定义:查询中嵌套查询就是子查询
注意:子查询必须用()括起来
子查询的本质:
a.内联视图
b.把子查询的结果作为外部查询的条件
--找出工资大于Mark的员工名字和工资
//1.查询出Mark的工资是多少?
select salary from s_emp where first_name='Mark';//1450
//2.以第一个查询的结果作为条件查询出最终结果
select * from s_emp where salary > 1450;
==>整合成子查询:
select * from s_emp where salary>
(select salary from s_emp where first_name='Mark');
--找出平均工资大于公司平均工资的部门编号
//1.公司平均工资
select avg(salary) from s_emp;//1255.08
//2.以第一个查询的结果作为条件查询出最终结果
select dept_id,avg(salary) from s_emp group by dept_id
having avg(salary)>1255.08;
==>整合成子查询:
select dept_id,avg(salary) from s_emp group by dept_id
having avg(salary)>(select avg(salary) from s_emp);
--找出与'Ben'同部门的员工
//1.查询出'Ben'所在的部门编号
select dept_id from s_emp where first_name='Ben';//43
//2.查询出部门编号为第一个查询结果的所有员工,并且把'Ben'去除
select * from s_emp where dept_id=43 and first_name<>'Ben';
==>整合成子查询:
select * from s_emp where dept_id=
(select dept_id from s_emp where first_name='Ben')
and first_name<>'Ben';
--查询出客户名,电话号码,以及订单数
//分组+关联
select c.name,c.phone,count(o.id) from s_customer c left join s_ord o
on o.customer_id=c.id group by c.name,c.phone;
//子查询完成
//相关子查询
select c.name,c.phone,
(select count(o.id) from s_ord o where o.customer_id=c.id)
from s_customer c
//无关子查询
select c.name,c.phone,a.sum from s_customer c join
(select c.id id,count(o.id) sum from s_customer c left join s_ord o
on o.customer_id=c.id group by c.id) a on a.id=c.id;
子查询的分类:
1.无关子查询:
内查询没有用到外查询的列,而且内查询可以单独运行.
2.相关子查询:
内查询使用了外查询的列,而且内查询不能单独运行.
子查询的特点:
1.子查询很灵活,可以解决很多其他查询方式不能解决的问题
2.子查询效率很低,其中相关子查询效率最低
3.子查询嵌套的层数越多,则效率越低
为什么相关子查询的效率极其低下?
内查询用到了外查询的列,每次查询行记录时都会迭代表格中
每一行的行记录,而这种迭代中产生的值都是动态生成的.
结论:
如果可以使用分组查询就不要使用子查询
如果一定要用子查询,则优先使用无关子查询,实在没有办法最后
选择相关子查询.
性能排序:
分组/关联查询>无关子查询>相关子查询
--找出各个部门中大于他所在部门平均工资的员工名和工资
//相关
select e.first_name,e.salary from s_emp e where e.salary>
(select avg(salary) from s_emp e1 where e1.dept_id=e.dept_id);
//无关
select e.first_name,e.salary from s_emp e join
(select dept_id,avg(salary) avg from s_emp group by dept_id) a
on a.dept_id=e.dept_id where e.salary>a.avg;
--找出职称相同的员工
select first_name,title from s_emp where title in
(select title from s_emp group by title having count(*)>=2);
------------------------------------------------------------------
rownum:
特点:永远从1开始,依次递增,从不产生间隔
select rownum,e.* from s_emp e;
select rownum,e.* from s_emp e where rownum = 1;
select rownum,e.* from s_emp e where rownum = 5;
select rownum,e.* from s_emp e where rownum <= 5;
select rownum,e.* from s_emp e where rownum >= 5;
rownum可以解决的问题:
1.top_N问题
解决思路:
1.利用子查询,先排序,后过滤
2.利用rownum的特性来直接取最前面几行的记录
--查询本公司工资前三的员工
select * from
(select first_name,salary from s_emp order by 2 desc)
where rownum <= 3;
--查询出订单价格最低的两个现金支付的订单
select * from
(select o.id,o.total,o.payment_type from s_ord o where o.payment_type='CASH'
order by 2) where rownum<=2;
2.分页问题
通项公式:
select outer_.* from
(
select rownum rownum_,core_.* from
(
select 列 from 表 where 条件 order by 字句;--核心业务
)
core_ where rownum<=endvalue
)
outer_ where outer_.rownum_>=startvalue;
--查询员工表第6到第10行记录
select outer_.* from
(
select rownum rownum_,core_.* from
(
select * from s_emp
)
core_ where rownum<=10
)
outer_ where outer_.rownum_>=6;
select outer_.* from
(select rownum rownum_,e.* from s_emp e where rownum<=10)
outer_ where outer_.rownum_>=6;
--找出在Asia工作的员工的第2行到第4行记录
select outer_.* from
(
select rownum rownum_,core_.* from
(
select * from s_emp e
join s_dept d on e.dept_id=d.id
join s_region r on d.region_id=r.id
where r.name='Asia'
)
core_ where rownum<=4
)
outer_ where outer_.rownum_>=2;
select a.* from
(
select rownum rownum_,first_name,salary from
(
select first_name,salary from s_emp where dept_id in
(
select id from s_dept where region_id=
(
select id from s_region where name='Asia'
)
)
)
)
a where a.rownum_>=2 and a.rownum_<=4;
---------------------------------------------------------
集合操作
如:t_test
id name
1 jack
2 rose
3 tom
4 mary
MINUS 求两个结果集的差
select * from t_test where id=1 or id=3
minus
select * from t_test where id>=3;
INTERSECT 求两个结果集的交集
select * from t_test where id=1 or id=3
intersect
select * from t_test where id>=3;
UNION 求两个结果集的并集(不包含重复结果)
select * from t_test where id=1 or id=3
union
select * from t_test where id>=3;
UNION ALL 求两个结果集的并集(包含重复记录)
select * from t_test where id=1 or id=3
union all
select * from t_test where id>=3;
---------------------------------------------------------
exists和not exists
在子查询中,使用在where字句,它只关心条件是否有存在的可能,
如果有可能,则返回true,反之则返回false
not exists与exists正好相反
--找出与'Ben'同部门的员工
select e.first_name,e.dept_id from s_emp e
where exists
(select 1 from s_emp e1 where e1.dept_id=e.dept_id
and e1.first_name='Ben')
and e.first_name <> 'Ben';
--找出各个部门工资排名前二的员工
解题思路:本部门中比'我'工资高的员工不超过一个
select e.dept_id,e.first_name,e.salary from s_emp e
where exists
(select 1 from s_emp e1 where e1.dept_id=e.dept_id and
e1.salary>e.salary having count(*)<=1)
order by 1;
select e.dept_id,e.first_name,e.salary from s_emp e
where not exists
(select 1 from s_emp e1 where e1.dept_id=e.dept_id and
e1.salary>e.salary having count(*)>1)
order by 1;
--找出各个部门工资最高的员工
解题思路:本部门中比'我'工资高的人没有
select e.dept_id,e.first_name,e.salary from s_emp e
where not exists
(select 1 from s_emp e1 where e1.dept_id=e.dept_id and
e1.salary>e.salary)
order by 1;
---------------------------------------------------------
其他运算符:
1.=any
--找出与41部门工资相同的其他部门的员工
2.all
--找出工资比41部门任何一个人都高的员工