Oracle_SQL(7) 复杂查询

时间:2021-11-28 15:02:18

1.rownum 伪列
<,<=
select * from emp where rownum<5;
取工资前3名的人员
select * from
(select * from emp order by sal desc)
where rownum<=3;
取每个部门工资的最大值
select deptno,max(sal) from emp group by deptno order by max(sal) desc

取每个部门工资最大的人员
select * from emp,
(select deptno,max(sal) sal from emp group by deptno) emax
where (emp.deptno=emax.deptno or emp.deptno is null and emax.deptno is null)
and emp.sal=emax.sal

取工资第二名的人员
select * from emp where sal=
(select max(sal) from emp where empno in
(select empno from emp
minus
select empno from emp where sal=(select max(sal) from emp)))

取每个部门工资第二的人员
select * from emp,
(select deptno,max(sal) sal from
(select * from emp
minus
select emp.* from emp,
(select deptno,max(sal) sal from emp group by deptno) emax
where (emp.deptno=emax.deptno or emp.deptno is null and emax.deptno is null)
and emp.sal=emax.sal)
group by deptno) esed
where emp.deptno=esed.deptno and emp.sal=esed.sal

获取工资2到5名的职员
select * from
(select emp.*,rownum rn from
(select * from emp order by sal desc) emp
where rownum<=5)
where rn>1

2.分页查询
&pages 当前页码
&counts 每页行数
select * from
(select emp.*,rownum rn from
(select * from emp order by empno) emp
where rownum<=&pages*&counts)
where rn>=(&pages-1)*&counts+1

3.工作日统计
declare
v_start varchar2(10);
v_end varchar2(10);
v_tianshu number;
v_zhou number;
v_yushu number;
v_zhoumo number:=0;
v_result number;
v_date date;
v_diji number;
begin
v_start:='&p_start';
v_end:='&p_end';
select to_date(v_end,'yyyy-mm-dd')-to_date(v_start,'yyyy-mm-dd') into v_tianshu from dual;
select trunc(v_tianshu/7),mod(v_tianshu,7) into v_zhou,v_yushu from dual;
if v_yushu!=0 then
v_date:=to_date(v_start,'yyyy-mm-dd')+v_zhou*7;
for rs in 0..v_yushu-1 loop
select to_char(v_date+rs,'d') into v_diji from dual;
if v_diji=1 or v_diji=7 then
v_zhoumo:=v_zhoumo+1;
end if;
end loop;
end if;
v_result:=v_zhou*5+v_yushu-v_zhoumo;
dbms_output.put_line('工作日:'||v_result);
end;

4.删除重复记录
有主键的表,除主键列外,其它列可能重复,主键列列。
create table emp1 as select * from emp;
alter table emp1 add constraints pk_emp1 primary key (empno);
insert into emp1 (empno,ename,deptno) (select empno+1000,ename,deptno from emp where deptno=20);
select * from emp1 order by deptno,empno;
连主键都没有的表,所有列都可能重复,只有伪列rowid不重复。
create table emp2 as select * from emp;
insert into emp2 (empno,ename,deptno) (select empno,ename,deptno from emp where deptno=30);
select emp2.*,rowid from emp2 order by deptno,empno;

对于重复记录来说,我们需要先确定判断重复的依据,比如ename。
select ename,count(*) from emp1 group by ename having count(*)>1;
select ename,count(1) from emp2 group by ename having count(1)>1

删除重复记录(1):有主键
删前边的记录,保留后边的记录;
delete from emp1 d where empno<any(select empno from emp_1 where ename=d.ename);
delete from emp1 d where exists (select empno from emp1 where ename=d.ename and d.empno<empno);

删后边的记录,保留前边的记录;
delete from emp1 d where empno>any(select empno from emp_1 where ename=d.ename);

删除重复记录(2):无主键
删前边的记录,保留后边的记录;
delete from emp2 d where rowid<any(select rowid from emp2 where ename=d.ename);
删后边的记录,保留前边的记录;
delete from emp2 d where rowid>any(select rowid from emp2 where ename=d.ename);

5.条件查询case when
select emp.*,case deptno
when 10 then '10部门名'
when 20 then '20部门名'
when 30 then '30部门名'
when 40 then '40部门名' end case
from emp;
=》
select emp.*,decode(deptno,10,'10部门名',20,'20部门名',30,'30部门名',40,'40部门名' ) from emp;

select emp.*,case
when sal<1000 then '工资小于1000'
when sal<2000 then '工资小于2000'
when sal<3000 then '工资小于3000'
else '工资大于等于3000' end case
from emp;
=》
select emp.*,decode(sign(sal-1000),-1,'工资小于1000' ,
decode(sign(sal-2000),-1,'工资小于2000' ,
decode(sign(sal-3000),-1,'工资小于3000' ,'工资大于等于3000')))
from emp;

6.with as 语句
作用:将重复使用的sql语句放置在with as 语句中,并设置别名,后续操作引用别名即可,以减少重复sql的执行次数。
with a as (select * from emp where deptno=30)
select * from a

7.父子查询
语法:
select * from table [start with condition1]
connect by [prior] id=parentid
说明:
[start with condition1]:用来限制第一层的数据,以第一层为基础来查找第二层,并以此类推
connect by [prior] id=parentid :用来指明父子关系,及用第一层的id和第二层的parentid匹配
connect by id=[prior] parentid :从下层往上层
level列:树形结构中的层级编号
connect_by_root(字段名)列:获取第一层集结点结果集中的任意字段的值
举例:
从上向下
select emp.*,level,connect_by_root(ename) from emp start with mgr is null
connect by prior empno=mgr
从下向上
select emp.*,level,connect_by_root(ename) from emp start with empno=7369
connect by empno=prior mgr

8.merge into插入或更新
合并两个表的数据,主键不存在则新增,存在则更新。
语法:
merge into 表1
using 表2
on (条件)
when not matched then
insert values(表2.列) where 表2.列=?
when matched then
update set 表1.列=表2.列 where 表1.列=表2.列

9.分析函数OVER (PARTITION BY
9.1 SUM *
SELECT mgr,sal,sum(sal) OVER (PARTITION BY mgr),
sum(sal) OVER (PARTITION BY mgr ORDER BY sal),
sum(sal) OVER (PARTITION BY mgr ORDER BY sal RANGE UNBOUNDED PRECEDING) l_csum
FROM emp;
--范围无限的前面
9.2 AVG *
select mgr,sal,avg(sal) OVER (PARTITION BY mgr),
avg(sal) OVER (PARTITION BY mgr ORDER BY sal),
avg(sal) OVER (PARTITION BY mgr ORDER BY sal ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
from emp;
--前后延1行
9.3 MAX *
SELECT ename,mgr,sal,
MAX(sal) OVER (PARTITION BY mgr) AS mgr_max
FROM emp;
--按照mgr分组
9.4 MIN *
SELECT ename,mgr,hiredate,sal,
MIN(sal) OVER(PARTITION BY mgr ORDER BY hiredate RANGE UNBOUNDED PRECEDING) as p_cmin
FROM emp;
--按照hiredate排序
9.5 COUNT *
SELECT ename,mgr,sal,
COUNT(*) OVER (PARTITION BY mgr ORDER BY sal RANGE UNBOUNDED PRECEDING) AS mov_count FROM emp;
--前面无限的范围