Oracle多表查询和子查询,分页,集合和行列转换

---多表查询中的一些概念
----笛卡儿积
select * from emp e, dept d;
----等值链接
select e.ename, e.sal, e.job, d.dname
from emp e, dept d
where e.deptno = d.deptno;
----不等值链接
select e.ename, e.sal, e.job, d.dname
from emp e, dept d
where e.deptno != d.deptno;
----显示出所有部门的信息
---oracle和mysql通用外链接
select e.ename, e.sal, e.job, d.dname
from dept d left join emp e
on d.deptno = e.deptno;
----oracle专用外链接
----要显示哪边的全部数据,就条件的对面写上(+)
select e.ename, e.sal, e.job, d.dname
from emp e, dept d
where e.deptno = d.deptno(+);

----显示出所有员工姓名,员工的领导姓名。
select e1.ename, e2.ename
from emp e1, emp e2
where e1.mgr=e2.empno;
----显示出所有员工姓名,员工部门名称,员工的领导姓名,领导的部门名称。
select e1.ename 员工姓名, d1.dname 员工部门名称, e2.ename 领导姓名,d2.dname 领导部门名称
from emp e1, emp e2, dept d1, dept d2
where e1.mgr=e2.empno
and e1.deptno = d1.deptno
and e2.deptno = d2.deptno;
----显示出所有员工姓名,员工部门名称,员工工资等级
----员工的领导姓名,领导的部门名称,领导工资等级。
select e1.ename 员工姓名, d1.dname 员工部门名称, s1.grade 员工工资等级,
       e2.ename 领导姓名, d2.dname 领导部门名称, s2.grade 领导工资等级
from emp e1, emp e2, dept d1, dept d2, salgrade s1, salgrade s2
where e1.mgr=e2.empno
and e1.deptno = d1.deptno
and e2.deptno = d2.deptno
and e1.sal between s1.losal and s1.hisal
and e2.sal between s2.losal and s2.hisal;

----显示出所有员工姓名,员工部门名称,员工工资等级
----员工的领导姓名,领导的部门名称,领导工资等级。
----要求工资等级显示汉字
select e1.ename 员工姓名, d1.dname 员工部门名称,
       case s1.grade
         when 1 then '一级'
           when 2 then '二级'
             when 3 then '三级'
               when 4 then '四级'
                 else '五级'
                   end 员工工资等级,
       e2.ename 领导姓名, d2.dname 领导部门名称, s2.grade || '级' 领导工资等级
from emp e1, emp e2, dept d1, dept d2, salgrade s1, salgrade s2
where e1.mgr=e2.empno
and e1.deptno = d1.deptno
and e2.deptno = d2.deptno
and e1.sal between s1.losal and s1.hisal
and e2.sal between s2.losal and s2.hisal;

----子查询
----子查询返回一个值
----查出工资和WARD一样的员工信息
---先差出WARD的工资
select sal from emp where ename = 'WARD';
---再查出和WARD一样的员工信息
select * from emp
where sal = (select sal from emp where ename = 'WARD');
----子查询返回一个集合
----查询出工资和10号部门任意一个员工一样的员工信息
----先查出10号部门员工工资集合
select sal from emp where deptno = 10;
---再查询出工资和10号部门任意一个员工一样的员工信息
select * from emp
where sal in (select sal from emp where deptno = 10);

-----如果拿子查询作为一个条件
-----可以写=号的地方,尽量用in。


----子查询返回一张表
----查出每个部门的平均工资,显示出每个部门的名称
----先查出每个部门的平均工资
select deptno, avg(sal)
from emp
group by deptno;
----再拿上面查出的表和dept表做联查
select t.*, d.dname
from (select deptno, avg(sal)
from emp
group by deptno) t, dept d
where t.deptno = d.deptno;

------查询出比7654工资高,和7788工作一样的员工信息
select * from emp
where sal>(select sal from emp where empno = 7654)
and job in (select job from emp where empno = 7788);

-----查询出每个部门最低工资,最低工资的员工的姓名,该员工的部门名称
-----先查出每个部门的最低工资
select deptno, min(sal)
from emp
group by deptno;
-----再拿上面查出的表和emp,dept联查
select e.ename, d.dname, e.sal
from emp e, dept d, (select deptno, min(sal) msal
    from emp
    group by deptno) t
where e.deptno = d.deptno
and d.deptno = t.deptno
and e.sal = t.msal;

----查出不是领导的员工。
---先查出领导的编号的集合
select mgr from emp;
----查出不是领导的员工【错误写法】
select * from emp
where empno not in (select mgr from emp);
-----如果要作为条件来用的集合中含有null值。那么该集合不能作为条件来用。
----第一种去null方法
select * from emp
where empno not in (select mgr from emp where mgr is not null);
----第二种去null方法
select * from emp
where empno not in (select nvl(mgr, '') from emp);
----第三种去null方法
select * from emp
where empno not in (
select e2.empno
from emp e1, emp e2
where e1.mgr = e2.empno);


-----如果拿子查询作为一个条件
-----可以写=号的地方,尽量用in。
-----用in的时候必须在后面加上 is not null。

----exists
----第一种exists后面跟一个布尔值
select * from emp where exists (select * from dept where deptno = 10);
select * from emp where exists (select * from dept where deptno = 50);
select * from emp where 1=2;
----第二种:查看子查询种外键有记录的数据。
----查询出有人的部门
----数据量大的时候,用exists效率特别高。
select * from dept d
where exists (select * from emp e where e.deptno = d.deptno);
---用in来实现查询出有人的部门
select * from dept d where deptno in
(select deptno from emp);
-----把emp表中最低工资的人去掉
select * from emp e1
where exists (select * from emp e2 where e1.sal>e2.sal);

----rownum :伪列,我们在做select操作的时候,每次查出一行数据,就在该数据加上一个序列
-------------必须从1开始,依次往后加,不能跳着加。
select rownum, e.* from emp e where rownum < 10;

-----查询出工资最高的前三人。
select rownum, e.* from emp e order by sal desc
----因为select操作要先于order by。所以当我们加上rownum之后。被order by把顺序弄乱了。
select rownum, t.* from
(select rownum, e.* from emp e order by sal desc) t
where rownum < 4;

select t.rownum, t.* from emp t;---【错误写法】
----rowid:行物理地址,永远不变。
select rowid, e.* from emp e;


-----查询emp表中比本部门平均工资高的员工
select e.*
from emp e,
(select deptno, avg(sal) asal
from emp
group by deptno) t
where e.deptno = t.deptno
and e.sal>t.asal;

------查出每年入职的员工个数
select to_char(e.hiredate, 'yyyy') y, count(1) n
from emp e
group by to_char(e.hiredate, 'yyyy');
----算出总的员工个数。
select sum(n) total
from (select to_char(e.hiredate, 'yyyy') y, count(1) n
from emp e
group by to_char(e.hiredate, 'yyyy'));
----把1987年这一行记录变成一列
select case y
       when '1987' then n
         end "1987"
from (select to_char(e.hiredate, 'yyyy') y, count(1) n
from emp e
group by to_char(e.hiredate, 'yyyy'));
-----把总数量total列和1987列。拼起来。
select sum(n) total,
       max(case y
       when '1987' then n
         end) "1987"
from (select to_char(e.hiredate, 'yyyy') y, count(1) n
from emp e
group by to_char(e.hiredate, 'yyyy'));
----把其余年份补齐
select sum(n) total,
       max(case y
       when '1987' then n
         end) "1987",
       min(case y
       when '1980' then n
         end) "1980",
       avg(case y
       when '1981' then n
         end) "1981",
       sum(case y
       when '1982' then n
         end) "1982"
from (select to_char(e.hiredate, 'yyyy') y, count(1) n
from emp e
group by to_char(e.hiredate, 'yyyy'));


----null值和任意一个数字相加都是null
----null+1=null ; +号是算术运算符,sum是多行函数。

---oracle中的分页
----每页五条数据,要第二页数据。【要emp表种6-10五行记录】  
select * from
  (select * from emp order by sal desc)
where rownum > 5;
----在做条件查询的时候,先执行where
----当我们select操作要查询出第一行记录的时候,
----本来应该在该行上添加一个为1的rownum。
----但是我们要拿1和where后面的条件1 > 5做比对。
----那么1>5成立吗?不成立,所以改rownum加不上。
----那么后面就不再查询了。

----最终结论。我们不能直接用rownum大于一个正数。
----但是我可以间接使用rownum大于一个正数

select * from (
    select rownum rn, tt.* from (
         select * from emp order by sal desc
    ) tt where rownum < 11
) where rn > 5;
---分页查询固定格式
select * from (
    select rownum rn, tt.* from (
        ---业务查询
    ) tt where rownum < 11
) where rn > 5;


-----集合运算
---查询出工资等级为一的员工
select e.*
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal;
---查询出工资等级为二的员工
select e.*
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;

-----union 并集【去重】
select e.*
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal
union
select e.*
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;
-----union all 并集【不去重】
select e.*
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal
union all
select e.*
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;


-----minus 差集
select e.*
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal
minus
select e.*
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;

-----intersect 交集
select e.*
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal
intersect
select e.*
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;


-----注意事项:集合运算的双方,查询的字段的数量必须一致,
----对应的类型也必须一致,如果其中一张表中没有对应的字段,用null或者没有意义的常量补齐。
select e.ename, e.sal
from emp e, salgrade s
where s.grade=1 and e.sal between s.losal and s.hisal
union
select e.ename, -1
from emp e, salgrade s
where s.grade=2 and e.sal between s.losal and s.hisal;


----拓展
create table student(
       xm varchar2(10),
       xk varchar2(10),
       fs number(3)
);

select * from student;
------查询出所有成绩都高于80的学生的姓名
----思路:先把数据变成两行记录,每行一个学生,把每科成绩都显示在一行上。
-----最终变成   姓名,语文,数学,英语,两行四列数据。
select xm from
(select s1.xm, s1.fs 数学, s2.fs 语文, s3.fs 英语
from student s1, student s2, student s3
where s1.xk='数学' and s2.xk='语文' and s3.xk='英语'
and s1.xm = s2.xm and s2.xm = s3.xm)
where 数学>80 and 语文>80 and 英语>80;

------思路:拿出每个学生最低分数,看最低分数是否大于80。
select s.xm, min(s.fs) sss
from student s
group by s.xm
having min(s.fs) > 80;

------思路: 查询出分数有比80低的学生
select distinct xm from student where xm not in(
select xm from student where fs < 80);

------思路: 条件表达式来做
-------------把每个分数高于80的变成1。否则变成0。
select xm, sum(case when fs>80 then 1 else 0 end) n, count(1)
from student
group by xm
having sum(case when fs>80 then 1 else 0 end)=count(1);

猜你喜欢

转载自blog.csdn.net/lyf_ldh/article/details/81140833