Oracle 查询优化改写

时间:2022-11-18 09:37:49

第一章 单表查询

1、将空值转换为实际值

    SELECT coalesce(comm,0) FROM emp;

说明coalesce与nvl、decode的区别
①coalesce(comm,0),若comm为空,则取0,否则返回comm;
适合多个参数,coalesce(EXPR1,EXPR2,EXPR3,…,EXPRn),所有参数类型必须保持一致;
coalesce可返回第一个不为空的值。
②nvl(comm,0)只适合2个参数;将空值转换为0;
③decode(comm,’apple’,0),若comm=apple,返回0,否则返回NULL

2、拼接列
用字符串“||”把各列连在一起

    SELECT ename ||'的工作是'||job AS msg FROM emp WHERE deptno = 10;
    ——————————————————
    CLARK的工作是MANAGER

3、限制返回的行数

SELECT * FROM (SELECT rownum AS an,emp.* FROM emp WHERE rownum <=2) WHERE sn = 2;

本语句返回的是第二行的数据,但是为什么先要写子查询呢?
因为rownum是依次对数据做标识的,需要先把所有的数据取出来,才能确认第二行。

4、随机返回n条记录

SELECT empno,ename FROM (SELECT empno,ename FROM emp ORDER BY dbmsandom.value()) WHERE rownum <= 3;

为什么要写一层嵌套?
对于随机返回n行数据,正确的写法应该是 先随机排序,再取数据 。否则每次随机返回的值会是一样的。

第二章 给查询结果排序

1、将返回结果排序

ORDER BY 1,2;

表示按照SELECT后的字段进行排序,1表示第一栏,2表示第二栏;
此处的意思是,先按照第一栏升序排列,在第一栏相同的情况下,按第二栏升序排列。

2、TRANSLATE
#语法格式
TRANSLATE(expr,from_string,to_string)

from_string 与 to_string 一一对应,如果在from_string中没有的,就不变。
如果to_string 为空,则返回空值;
如果to_string 对应的没有字符,删除from_string中列出的字符将会被消掉。

SELECT TRANSLATE('ab你好bcadefg','1abcdefg','1')AS NEW_STR FROM DUAL;
NEW_STR
------
你好

“你好”不进行替换,其他字符对应位置均为空,故替换为空

3、处理排序空值
用 关键字NULLS FIRST 和NULLS LAST
空值在前 NULLS FIRST
空值在后 NULLS LAST

第三章 使用数字

1、正确的平均值
avg(coalesce(comm,0)) /需要把空值转换为0/
聚合函数会忽略空值,对avg,count会产生影响,根据需求决定把空值转换为零。

2、group by
如果select 后面有聚合函数,通常需要加group by;
当表中没有数据时, 不加group by会返回一行数据,但加了group by 会没有数据返回。

3、计算累计差
可将需要减的数,先变成负数,用CASE WHEN

4、dense_rank()
返回排序值,若有相同值,可都返回

dense_rank() over(PARTITION BY deptno ORDER BY sal DESC) 

5、计算出现次数最多的值
比如查看部门中哪个工资等级的员工最多,需要分4步进行

SELECT deptno,sal FROM(select deptno,sal dense_rank() over(PARTITION BY deptno ORDER BY 出现次数 DESC) 次数排序 FROM (SELECT sal,deptno,count(*) 出现次数 FROM emp GROUP BY deptno,sal)x)y where 次数排序 = 1

第一步:计算不同工资出现的次数
第二步:按次数排序生成序号
第三步:根据序号过滤得到需要的结果
第四步:利用partition by 子句分别查询各部门哪个工资等级的员工最多

6、返回最值所在行数据
①标量–先取出最大值,再与此最大值关联,略麻烦

SELECT deptno, empno, (SELECT MAX(b.ename) FROM emp b WHERE b.sal = a.max_sal)工资最高的人, ename, sal FROM (SELECT deptno, empno, MAX(sal) over (PARTITION BY deptno) max_sal, ename, sal FROM emp a WHERE deptno = 10)a ORDER BY 1,5 DESC;

②分析函数

SELECT deptno, empno, MAX(ename)keep(dense_rank FIRST ORDER BY sal)over (PARTITION BY deptno) 工资最低的人, MAX(ename)keep(dense_rank LAST ORDER BY sal)over (PARTITION BY deptno) 工资最高的人, ename, sal FROM emp WHERE deptno = 10 ORDER BY 1,6 DESC;

KEEP函数可以满足此要求,且可同时返回最大值和最小值;
另外,frist、last语句也可放在group 里与其他聚合函数一样使用,这时要去掉后面的over (partition by **)

7、求总和的百分比
总工资合计需要用到分析函数:sum()和over()
当over()后不加任何内容时,就是对所有的数据进行汇总。

SELECT deptno 部门, 工资合计, 总合计, round((工资合计/总合计)*1002) 工资比例 FROM (SELECT deptno,工资合计,SUM(工资合计) over() 总合计 FROM(SELECT deptno,SUM(sal) 工资合计 FROM emp GROUP BY deptno)x)y ORDER BY 1;

第一步:分组汇总
第二步:通过分析函数获取总合计
第三步:得到总合计周就可以计算比例

另外,可以用专用的比例函数“ratio_to_report”来直接计算

SELECT deptno, empno, ename, sal, round(ratio_to_report(sal) over(PARTITION BY deptno)*100,2) 工资比例 FROM emp ORDER BY 1,2;

第四章 操作多个表

1、union all与空字符串
union all常用来于合并多个数据集,空字符串本身是varchar2类型,null可以是任何类型,故二者不等价。

2、union 与or
将or语句进行改写,如果改写成union all结果就是错的,因为 or是两个结果的合集而不是并集,故改写时需要改为union来去掉重复的数据。
*但是!*不仅2个 数据集间重复的数据会被去重,而且单个数据集里面重复的数据也会被去重,有重复数据集用UNION后得到的数据与预期会不一致。
所以,需要在去重前加一个可以唯一标识各行的列即可。
加入唯一列,既可保证正确的去重,也可防止不该发生的去重。
除了用唯一列、主键列外,还可以用rowid。
还可以增加rownum来当做唯一列。

3、组合相关的列
当有多个表关联时,join的方式更能清楚的看清各表之间的关系,建议优先使用JOIN的写法。

4、IN、EXISTS和 INNER JOIN
此3种写法并没有固定的哪种写法更高效,在写的时候,需要查看PLAN。

5、INNER JOIN、LEFT JOIN 、RIGHT JOIN、FULL JOIN
INNER JOIN返回2个表匹配的数据;
LEFT JOIN只返回与左表匹配的数据;

  select 1.str left_str,r.str right_str from 1,r where 1.v=r.v(+) order by 1,2;
  --以1为基准表,返回2中与1匹配的数据 

RIGHT JOIN返回与右表匹配的数据;

select 1.str left_str,r.str right_str from 1,r where 1.v(+)=r.v order by 1,2;
  --以2为基准表,返回1中与r匹配的数据 

FULL JOIN无(+)的写法;该方式均返回所有的数据,但只有相匹配的数据显示在同一行, 非匹配的行只显示一个表的数据。

6、自关联
自关联的话,其实就是2次查询同一个表,但是取不同的别名。用JOIN连接。

7、检查两个表中的数据及对应数据的条数是否相同
比较2个数据集的不同时,通常用类似下面的FULL JOIN语句

   SELECT v.empno,v.ename,b.empno,b.ename FROM v FULL JOIN emp b ON (b.empno = v.empno) WHERE (v.empno IS NULL OR b.empno IS NULL)

有时需要对数据进行处理,再进行比较,比如上述语句再加一列显示相同数据的条数,再进行比较。

SELECT v.empno,v.ename,v.cnt,emp.empno,emp.ename,emp.cnt FROM (SELECT empno,ename,COUNT(*) cnt FROM v GROUP BY empno,ename)v FULL JOIN (SELECT empno,ename,COUNT(*) cnt FROM emp GROUP BY empno,ename)emp ON (emp.empno = v.empno AND emp.cnt = v.cnt) WHERE (v.empno IS NULL OR emp.empno IS NULL)

8、聚集与内连接
*有点没看懂,改天更

9、聚集与外连接
先做聚集操作,再外连接即可。

SELECT e.deptno, SUM(e.sal) total_sal, SUM(e.sal * eb2.rate) total_bonus FROM emp e LEFT JOIN(SELECT eb.empno, SUM(CASE WHEN eb.type = 1 THEN 0.1 WHEN eb.type = 2 THEN 0.2 WHEN eb.type = 3 THEN 0.3 END) rate FROM emp_bonus eb GROUP BY eb.empno)eb2 ON eb2.empno = e.empno GROUP BY e.deptno ORDER BY 1;