sql一些基本查询问题

注:一下所有查询均是基于oracle的scott用户下自带的一些表的操作
什么是分组函数?
分组函数作用于一组数据,并对一组数据返回一个值,常用的分组函数:avg(),sum(),min(),max(),count(),wm_count()
avg()求平均值,sum()求和 例如:求员工的平均工资和工资的总额

select * from emp;
select avg(sal),sum(sal) from emp;
select max(sal),min(sal) from emp;
select count(*),count(1),count(comm) from emp;
select count(deptno) from emp;
select count(distinct deptno) from emp;

wm_concat()行转列,完成字符串的拼接

select deptno,wm_concat(ename) from emp group by deptno;

分组函数与空值count(列值自动去空),分组函数也会自动去空,如果想让它不去空可以使用nvl()函数
nvl()函数使分组函数无法过滤空值

select avg(comm),sum(comm)/count(comm),sum(comm)/count(*) from emp;
select avg(comm),sum(comm)/count(nvl(comm,0)),sum(comm)/count(*) from emp;

分组数据。求每个部门的平均工资 套路公式:select a,avg(b) from emp group by a;
如果所查询的列不在分组函数中,那么它必须在分组数据中(即group by子句中)
反之:如果包含在group by子句中的列不必包含在select列表中

select deptno,avg(sal) from emp group by deptno;

使用多个列分组 求不同部门职位的平均工资

select deptno,job, avg(sal) from emp group by deptno,job order by deptno;

过滤分组数据 求平均工资大于2000的部门 having子句,
where和having的区别,不能在where子句中使用组函数,可以在having子句中使用组函数

select deptno,avg(sal) from emp group by deptno having avg(sal)>2000;

在没有分组的情况下,where和having可以通用,having先分组后过滤 where 先过滤后分组,
从sql优化的角度来说最好使用where

select deptno,avg(sal) from emp where deptno=10 group by deptno;
select deptno,avg(sal) from emp group by deptno having deptno=10;

在分组函数中使用排序 例如:统计每个部门的平均工资,按部门号升序

select deptno,avg(sal) from emp group by deptno order by deptno;
select deptno,avg(sal) from emp group by deptno order by 2;

分组函数的嵌套 例如:求最高平均工资

select  max(avg(sal)) from emp group by deptno;

group by 语句的增强 :
语法:group by rollup(a,b) 等价于group by a,b + group by a + group by null

select deptno,job,sum(sal) from emp group by deptno,job order by deptno;
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);

多表查询 笛卡尔积 等值连接 不等值连接 自连接 外连接 层次查询,
笛卡尔积 多表之间查询没有连接条件,得到结果是多表之间记录数的乘积,在笛卡尔积中会有很多错误的数据

select * from emp;
select * from dept;
select e.*,d.* from emp e,dept d;

等值连接 应该避免使用笛卡尔积,使用表与表之间的等值连接(表与表之间的连接条件至少应为表的数目-1)

select e.*,d.* from emp e,dept d where e.deptno=d.deptno;

不等值连接 例如:查询员工信息 要求显示:员工号,姓名,薪水,薪水的级别

select * from emp;
select * from salgrade;
select e.empno,e.ename,e.sal,g.grade from emp e ,salgrade g where e.sal>=g.losal and e.sal<=g.hisal;
select e.empno,e.ename,e.sal,g.grade from emp e ,salgrade g where e.sal between g.losal and g.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;
select d.deptno,d.dname,count(e.empno) from dept d left join emp e on e.deptno=d.deptno group by d.deptno,d.dname;

自连接 把自身看成两张不同的表 不适合操作大表

select e1.ename 员工姓名,e2.ename 老板姓名 from emp e1,emp e2 where e1.mgr=e2.empno;

层次查询 在某些情况下可以取代自连接,本质上是一个单表查询,
自连接和层次查询优缺点比较: 自连接查询的结果比较清晰,缺点:容易产生笛卡尔积,不适合操作大表,
层次查询,本质是一个单表查询,缺点:查询结果不直观

select level,e.empno,e.ename,e.mgr from emp e connect by prior empno=mgr start with mgr is null order by 1;

子查询 查询员工表中比scott工资高的员工

select * from emp;
select e.* from emp e where e.sal>(select e1.sal from  emp e1 where e1.ename='SCOTT'); 

子查询注意的10个问题
1.子查询语法中的小括号

select e.* from emp e where e.sal>(select e1.sal from  emp e1 where e1.ename='SCOTT'); 

2.子查询的书写风格

select e.*
  from emp e
 where e.sal > (select e1.sal from emp e1 where e1.ename = 'SCOTT');

3.可以使用子查询的位置:where ,select,having,from
在select子句中查询出来的结果必须是单行结果。

select e.empno,e.ename,e.sal,(select e1.job from emp e1 where e1.empno=7788 ) from emp e;

4.不可以使用子查询的位置:group by 注意下面的语句是错误的

select deptno,avg(sal) from emp group by (select deptno from  emp);

5.from 后的子查询

select * from (select empno,ename,sal,sal*12 annual from emp);

6.主查询和子查询可以不是同一张表 例如:查询部门名称是销售部的员工信息
两种方式:可以使用子查询也可以使用多表查询

select * from emp e where e.deptno=(select d.deptno from dept d where d.dname='SALES');
select e.* from emp e,dept d where e.deptno=d.deptno and d.dname='SALES';

7.一般不在子查询中使用排序;但在top-N问题分析中,必须对子查询排序(例如:找出员工表中工资最高的前三名)
关于行号注意的问题:1.行号永远按照默认的顺序生成 2.行号只能使用<,<= 不能使用>,>=

select t.*, rownum
  from (select * from emp order by sal desc) t
 where rownum <= 3;
 select rownum, empno, ename, sal
   from (select * from emp order by sal desc)
  where rownum <= 3;

8.一般先执行子查询,再执行主查询,但相关子查询例外 例如:找到员工表中薪水大于本部门平均薪水的员工

select empno,ename,sal,(select avg(sal) from emp where deptno=e.deptno) avgsal from emp e where e.sal>(select avg(sal) from emp where deptno=e.deptno );

9.单行子查询只能使用单行操作符;多行子查询只能使用多行操作符,
单行操作符:=,>,>=, <, <=,<> 返回单条记录,
多行操作符:in(等于列表中的任何一个) any(和子查询返回的任意一个值比较) all(和子查询返回的所有值比较),
实例1:查询员工信息, 要求职位与7566一样,薪水大于7782员工的薪水,

select * from emp e where e.job=(select job from emp where empno=7566) and e.sal>(select sal from emp where empno=7782);

实例2:查询工资最低的员工

select * from emp e where e.sal=(select min(sal) from emp);

实例3:查询最低工资大于20号部门最低工资的部门号和部门的最低工资

select deptno,min(sal) from emp group by deptno having min(sal)> (select min(sal) from emp  where deptno=20);

多行操作符使用示例 查询部门名称是SALES和ACCOUNTING的员工信息,

select * from emp e where e.deptno in (select d.deptno from dept d where d.dname='SALES' or d.dname='ACCOUNTING');
select e.* from emp e,dept d where e.deptno=d.deptno and (d.dname='SALES' or d.dname='ACCOUNTING');

下面的例子大于任意一个可以转单行只要大于子查询的最小值即可,

select * from emp e where e.sal> any(select sal from emp where deptno=30);
select * from emp e where e.sal> (select min(sal) from emp where deptno=30);
select * from emp e where e.sal> all(select sal from emp where deptno=30);
select * from emp e where e.sal> (select max(sal) from emp where deptno=30);

10.子查询是null值问题
10.1.单行子查询中的空值问题,

select * from emp where job=(select job from emp where ename='tom');

10.2.多行子查询中的空值问题 示例:查询不是老板的员工,
注意:有空值的情况下不要使用not in,因为not in等同于 不等于所有(<>all)
例如 a not in (10,20,null) 等价于 a!=10 and a!=20 and a!=null 因为判断是否为空不能使用!=,所以最后一个条件永远为假,

select * from emp where empno not in (select mgr from emp where mgr is not null);

查询所有是老板的员工,
空值作为子查询的一部分不是一个问题,如果你使用in操作符,因为in 操作符等同于any,

select * from emp where empno in (select mgr from emp);

分页查询 工资倒叙 一页4条,查询第二页 借助oracle的伪列,

select * from emp order by sal desc;

注意下面的查询 r是t2表的第一列(也可理解t1表的伪列) rn是t1表的第一列(也可理解e表的伪列),

select t2.*
  from (select rownum r, t1.*
          from (select rownum rn, e.* from emp e order by sal desc) t1
         where rn <= 8) t2
 where r > 4;

查询薪水大于本部门员工平均薪水的员工,
方法一:使用相关子查询:

explain plan for 
select e.empno,
       e.ename,
       e.sal,
       (select avg(sal) from emp where deptno = e.deptno) avgsal
  from emp e
 where e.sal > (select avg(sal) from emp where deptno = e.deptno);
 select * from table(dbms_xplan.display);

方法二:使用多表的联合查询

explain plan for 
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;
select * from table(dbms_xplan.display); 

按部门统计员工人数

select * from emp;
select count(*) from emp e where to_char(e.hiredate,'yyyy')='1981';

使用函数解决

select count(*) Total,
       sum(decode(to_char(e.hiredate, 'yyyy'), '1980', 1, 0)) "1980",
       sum(decode(to_char(e.hiredate, 'yyyy'), '1981', 1, 0)) "1981",
       sum(decode(to_char(e.hiredate, 'yyyy'), '1982', 1, 0)) "1982",
       sum(decode(to_char(e.hiredate, 'yyyy'), '1987', 1, 0)) "1987" from emp e;

使用子查询解决

select (select count(*) from emp) Total,
       (select count(*)
          from emp e
         where to_char(e.hiredate, 'yyyy') = '1980') "1980",
       (select count(*)
          from emp e
         where to_char(e.hiredate, 'yyyy') = '1981') "1981",
       (select count(*)
          from emp e
         where to_char(e.hiredate, 'yyyy') = '1982') "1982",
       (select count(*)
          from emp e
         where to_char(e.hiredate, 'yyyy') = '1987') "1987"
  from dual;

instr(a,b) 函数 如果字符串b在字符串a里面,则返回的是b在a中的位置,即返回值大于0

猜你喜欢

转载自blog.csdn.net/liyufu318/article/details/80058782