子查询
子查询是一条SELECT语句,但它是嵌套在其他SQL语句中的,为的是给该SQL提供数据以支持其执行操作。
查看谁的工资高于”CLARK”
SELECT ename,sal
FROM emp
WHERE sal>(SELECT sal
FROM emp
WHRER ename='CLARK');
查看与CLAEK同职位的员工
SELECT ename,job
FROM emp
WHERE job=(SELECT job
FROM emp
WHRER ename='CLARK');
在DDL中使用子查询
可以根据子查询的结果集快速创建一张表
创建表employee,表中字段为:empno,ename,job,sal,deptno,danme,loc 数据为现有表中emp与dept对应的数据
CREATE TABLE employee
AS
SELECT e.empno,e.ename,e.job,e.sal,e.deptno,d.dname,d.loc
FROM emp e,dept d
WHERE e.deptno=d.deptno(+);
DESC employee;
SELECT * FROM employee;
创建表时如果子查询中的字段有别名则该表对应的字段就使用该名作为字段名。
当子查询中一个字段含有函数或表达式,那么该字段必须给别名。
CREATE TABLE employee
AS
SELECT e.empno id ,e.ename name ,e.job,e.sal*12 salary ,e.deptno,d.dname,d.loc
FROM emp e,dept d
WHERE e.deptno=d.deptno(+);
DESC employee;
SELECT * FROM employee;
DML中使用子查询
DELETE FROM emp
WHERE deptno=(SELECT deptno FROM emp WHERE ename='CLARK');
子查询在WHERE字句中
查找薪水比整个机构平均薪水 高的员工
SELECT depno,ename,sal
FROM emp
WHERE sal>(SELECT AVG(sal) FROM emp );
子查询根据查询结果集的不同可以分为:
单行单列子查询:常用于过滤条件,可以配合=,>,>=,<,<=使用
多行单列子查询:常用于过滤条件,由于查询出多个值,在判断=时要配合IN , 判断>,>=等操作要配合ANY,ALL
多行多列子查询:常当做一张表看待
查询与SALESMAN同部门的其他职位员工:
SELECT ename,depno,job
FROM emp
WHERE depno IN (SELECT depno FROM emp WHERE job='SALESMAN')
AND job<>'SALESMAN';
查看比职位CLERK 和SALESMAN工资都高的员工
SELECT ename,job
FROM emp
WHERE sal>ANY(SELECT sal FROM emp WHERE job IN('CLERK',' SALESMAN'));
EXISTS关键字
EXISTS后面跟一个子查询,当该子查询可以查询出至少一条记录时,则EXISTS表达式成立并返回true
查看有员工的部门信息
SELECT deptno,dname
FROM dept d
WHERE
EXISTS (SELECT * FROM emp e WHERE d.deptno=e.deptno) ;
查看每个部门的最低薪水是多少?前提是该部门的最低薪水要高于30号部门的最低薪水
SELECT MIN(sal),deptno
FROM emp
HAVING MIN(sal)>(SELECT MIN(sal) FROM emp e WHERE deptno=30);
子查询在FROM子句中使用
查看比自己所在部门平均工资高的员工
SELECT e.ename ,e.sal,e.deptno
FROM emp e,(SELECT AVG(sal) avg_sal ,deptno FROM emp GROUP BY deptno) t
WHERE e.deptno=t.deptno
AND e.sal>t.avg_sal;
在子句中使用子查询,可以将查询的结果当做外层查询记录中的一个字段值显示
SELECT e.ename,e.sal,(SELECT d.name FROM dept d WHERE d.deptno=e.deptno) dname
FROM emp e
分页查询
分页查询是将查询表中数据分段查询,而不是一次性将所有数据查询出来。
有时查询的数据量非常大,这会导致系统资源消耗大,响应速度长,数据冗余严重。
为此当遇到这种情况时一般使用分页查询解决。数据库基本都支持分页,但是不同数据库语法不同。
ORACLE中的分页是基于伪列ROWNUM实现的。
ROWNUM不存在于任何一张表中,但是所有的表都可以查询该字段。该字段的值是随着查询自动生成的,方式是:每当可以从表中查询出一条记录时,该字段的值即为该条记录的行号,从1开始逐次递增。
SELECT ROWNUM,empno,ename,sal,job
FROM emp
在使用ROWNUM对结果集进行编号查询过程中不能使用ROWNUM做>1以上的数字判断,否则将查询不出任何数据
SELECT ROWNUM,empno,ename,sal,job
FROM emp
WHERE ROWNUM>1 ;
SELECT *
FROM( SELECT ROWNUM rn ,empno,ename,sal,job
FROM emp)
WHERE rn BETWEEN 6 AND 10;
排序需求需要子查询嵌套实现
查看工资排名的第6-10名
SELECT *
FROM(SELECT ROWNUM rn,t.*
FROM( SELECT ,empno,ename,sal,job
FROM emp
ORDER BY sal DESC ) t)
WHERE rn BETWEEN 6 AND 10;
改进:
SELECT *
FROM(SELECT ROWNUM rn,t.*
FROM( SELECT ,empno,ename,sal,job
FROM emp
ORDER BY sal DESC ) t
WHERE ROWNUM<=10)
WHERE rn >=6;
根据要查看的页数,计算起点值start和终点值end替换掉BETWEEN和AND的参数,即得到当前页的记录。
计算区间公式:
pageSize:每页显示的条目数
page:页数
start:(page-1)*pageSize+1
end:pageSize*page
DECODE函数
可以实现分支效果(类似switch语句)
SELECT ename,job,sal,
DECODE(job,
'MANAGER',sal*1.2,
'ANALYST',sal*1.1,
'SALESMAN',sal*1.0
) bonus
FROM emp;
和DECODE函数功能相似的有CASE语句,实现类似if else操作。
SELECT ename,job,sal,
CASE job WHEN 'MANAGER' THEN sal*1.2
WHEN 'ANALYST' THEN sal*1.1
WHEN 'SALESMAN' THEN sal*1.0
ELSE sal END
bonus
FROM emp;
DECODE在GROUP BY中的应用
可以将字段值不同的记录看做一组
统计人数,将职位是‘MANAGER’,‘ANALYST’看做一组,其余职业看做另一组分别统计人数。
SELECT
COUNT(*),DECODE(job,
'MANAGER', 'VIP',
'ANALYST',' VIP',
'OTHER')
FROM emp
GROUP BY DECODE(job,
'MANAGER', 'VIP',
'ANALYST',' VIP',
'OTHER');
dept表中按照”OPERATIONS”,ACCOUNTING”,”SALES”排序
SELECT deptno,dname,loc
FROM dept
ORDER BY DECODE(dname,
'OPERATIONS',1,
'ACCOUNTING',2,
'SALES',3
);
排序函数
排序函数允许对结果集按照指定字段分组,在组内再按照指定的字段排序,最终生成组内编号。
ROW_NUMBER()函数生成组内连续且唯一的数字
查看每个部门的工资排名
SELECT ename,sal,deptno,
ROW_NUMBER() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp;
RANK 函数,生成组内不连续也不唯一的数字,同组内排序字段值一样的记录,生成的数字也一样。
SELECT ename,sal,deptno,
RANK() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp;
DENSE_RANK函数生成组内连续但不唯一的数字。
SELECT ename,sal,deptno,
DENSE_RANK() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp;
集合操作
UNION,UNION ALL
合并职位是MANGER的员工和薪水大于2500的员工集合
SELECT ename,job,sal
FROM emp
WHERE job='MANAGER'
UNION
SELECT ename,job,sal FROM emp
WHERE sal>2500;
如果有重复记录数,UNION ALL会出现重复记录数
SELECT ename,job,sal
FROM emp
WHERE job='MANAGER'
UNION ALL
SELECT ename,job,sal FROM emp
WHERE sal>2500;
INTERSECT 保留两个结果集的交集
列出职位是MANGER的员工并且薪水大于2500的员工
SELECT ename,job,sal
FROM emp
WHERE job='MANAGER'
INTERSECT
SELECT ename,job,sal FROM emp
WHERE sal>2500;
MINUS
获取两个结果集的差集
只有在第一个结果集中存在,第二个结果集不存在的数据才能够被显示出来,也就是结果集一减去结果集二的结果
列出职位是MANGER的但薪水低于2500的员工记录
SELECT ename,job,sal
FROM emp
WHERE job='MANAGER'
MINUS
SELECT ename,job,sal FROM emp
WHERE sal>=2500;
高级分组函数
高级分组函数用在GROUP BY 子句中,每个高级分组函数都有一套分组策略。
SELECT year_id,month_id,day_id,sales_value
FROM sales_tab
ORDER BY year_id,month_id,day_id;
查看每天的营业额
SELECT year_id,month_id,day_id,SUM(sales_value)
FROM sales_tab
GROUP BY year_id,month_id,day_id
ORDER BY year_id,month_id,day_id;
查看每月的营业额
SELECT year_id,month_id,SUM(sales_value)
FROM sales_tab
GROUP BY year_id,month_id
ORDER BY year_id,month_id;
查看每年的营业额
SELECT year_id,SUM(sales_value)
FROM sales_tab
GROUP BY year_id
ORDER BY year_id;
查看总的营业额
SELECT SUM(sales_value)
FROM sales_tab;
查看每天,每月,每年以及总共的营业额
ROLLUP():分组原则,参数逐次递减,一直到所有参数都不要,每一种分组都统计一次结果并且并在一个结果集中显示。
GROUP BY POLLUP(a,b,c)
相当于
GROUP BY a,b,c
UNION ALL
GROUP BY a,b
UNION ALL
GROUP BY a
UNION ALL
全表
SELECT year_id,month_id,day_id,SUM(sales_value)
FROM sales_tab
GROUP BY POLLUP( year_id,month_id,day_id);
CUBE():每种组合分一次组
分组次数是2的参数分数次方
GROUP BY CUBE(a,b,c)
abc
ab
bc
ac
a
b
c
全表
SELECT year_id,month_id,day_id,SUM(sales_value)
FROM sales_tab
GROUP BY CUBE( year_id,month_id,day_id);
我想仅查看每天与每月的营业额?
GROUPING SETS():每个参数是一种分组方式,然后将这些分组统计后并在一个结果集显示。
SELECT year_id,month_id,day_id,SUM(sales_value)
FROM sales_tab
GROUP BY
GROUPING SETS (( year_id,month_id,day_id),( year_id,month_id));