首页 > 代码库 > sql优化点整理

sql优化点整理

此文是我最早开始sql优化至今整理的小知识点和经常遇到的问题,弄懂这些对优化大型的sql会有不少帮助
---------------------------------使用了多余的外连接-------------------------------------------------
使用多余的外连接
外连接是一个代价非常昂贵的执行过程。如果业务需要,这种操作是必要的,但是有时
候会出现人为的在SQL 中使用不必要的外连接,这实际上是因为有的开发人员担心遗漏一
些数据而刻意使用它,这就非常有可能留下隐患,让数据库选择昂贵的执行计划而不是最优
的那一个。
SQL> select * from t1;
A B
---------- ----------
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
已选择9 行。
SQL> select * from t2;
C D
---------- ----------
   1001
2 1002
   1003
4 1004
   1005
6 1006
   1007
8 1008
   1009
已选择9 行。
 
通过下面这条语句,通过使用A 字段和T2 表C 字段关联,我们获取了T1 表上所有的
行以及T2 表上符合条件的行:
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c(+) ;
A B C D
---------- ---------- ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
1 101
3 103
5 105
7 107
9 109
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c(+) and t2.d>1000;
A B C D
---------- ---------   ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
这条SQL 的意思是告诉数据库,我要得到T1 表上所有的行,并且用A 列和T2 表C
做关联,同时要求T2 表C 列的值大于1000.
让我们再看看另一条结果集完全一样的SQL:
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c and t2.d>1000;
A B C D
---------- ---------- ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
从结果集上来看,这是两条等价的SQL 语句,就是说,在这种情况下,外连接其实是
没有用的,是人为的在SQL 里设定的限制!如果仔细看一下第一条语句,我们不难发现,
条件中T2.C>1000 已经明确的指出,在结果集中,T2 表在任何一行,C 列都应该有值的,
也就是在这种情况下,根本就不需要使用外连接,业务逻辑上讲,外连接在这里是多余的。
这种情况在开发人员的代码中有时候会遇到,对他们来讲,只要保证结果集正确就好,但对
数据库来讲,在执行时可能会引起极大的性能差别。
 
 
 
---------------------------------all_rows / first_rows-------------------------------------------------
对于OLAP 系统,绝大多少时候数据库上运行着的是报表作业,执行基本上是聚合类的SQL
操作,比如GROUP BY,这时候,把优化器模式设置成all_rows 是恰当的。
而对于一些分页操作比较多的网站类数据库,设置成first_rows 会更好一些。
 
在SQL 里通过Hints 的方式来将优化模式转换成FIRST_ROWS 
比如这样的一个每次取出10 条记录的分页查询:
Select * from
(SELECT /*+ first_rows(10) */ a.*,rownum rnum from
(SELECT /*+ first_rows(10) */ id,name from t1 order by id) a
Where rownum<=10)
Where rnum>=1;
 
alter session set optimizer_mode=all_rows;
alter session set optimizer_mode=first_rows;
 
 
 
---------------------------------join与left join-------------------------------------------------
当两表关联时,如a left join b,然后在b上有where条件,那么可去掉left。
不去掉left,会产生不必要的关联,严重的是,a left join b,一般会选择a表为驱动表,
这是为防止结果错误(当然优化器有可能看到where有b的条件去掉left)
无where时:
SQL> select * from t1
  2  left join t2
  3  on t1.a=t2.a
  4  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
         3          3          3          3
         1          1
 
SQL> select * from t1
  2    join t2
  3  on t1.a=t2.a
  4  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
         3          3          3          3
 
where条件在t2表的关连建上:
SQL> select * from t1
  2    join t2
  3  on t1.a=t2.a
  4  where t2.a=2
  5  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
 
SQL> select * from t1
  2  left  join t2
  3  on t1.a=t2.a
  4  where t2.a=2
  5  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
 
where条件在t2表的非关连建上:
SQL> select * from t1
  2    join t2
  3  on t1.a=t2.a
  4  where t2.b=2
  5  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
 
SQL> select * from t1
  2  left  join t2
  3  on t1.a=t2.a
  4  where t2.b=2
  5  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
 
 
以下2种写法等价:
SQL> select * from t1
  2  left  join t2
  3  on t1.a=t2.a
  4  and t2.a=2
  5  ;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
         3          3
         1          1
 
SQL> select * from t1,t2 where t1.a=t2.a(+) and t2.a(+)=2;
         A          B          A          B
---------- ---------- ---------- ----------
         2          2          2          2
         3          3
         1          1
 
 
left join on and on 与 left join on where 的区别:
第一种是:先根据on过滤,再关联
第二种是:先关联再根据where过滤
这2种写法意义是不同的,
inner jion没这个特殊性,则条件放在on中和where中,返回的结果集是相同的
实验:
创建2张表
SQL> select * from table1;
        ID    ID_NAME
---------- ----------
         1         10
         2         20
         3         30
 
SQL> select * from table2;
   ID_NAME NAME
---------- ----------
        10 AAA
        20 BBB
        30 CCC
 
第一种写法:t1只和t2的AAA关联,但还是显示所有行
SQL> select * from table1 t1
  2  left join table2 t2
  3  on t1.id_name=t2.id_name
  4  and t2.name=‘AAA‘;
 
        ID    ID_NAME    ID_NAME NAME
---------- ---------- ---------- ----------
         1         10         10 AAA
         3         30
         2         20
即使and使用t1,因为left join,也会留下所有行
SQL> select * from table1 t1
  2  left join table2 t2
  3  on t1.id_name=t2.id_name
  4  and t1.id=1;
 
        ID    ID_NAME    ID_NAME NAME
---------- ---------- ---------- ----------
         1         10         10 AAA
         2         20
         3         30
 
SQL> select * from table1 t1
  2  left join table2 t2
  3  on t1.id_name=t2.id_name
  4  and t1.id=0;
 
        ID    ID_NAME    ID_NAME NAME
---------- ---------- ---------- ----------
         2         20
         3         30
         1         10
第二种写法:t1和t2所有行关联,结果集再过滤,所以只留下1行
SQL> select * from table1 t1
  2  left join table2 t2
  3  on t1.id_name=t2.id_name
  4  where t2.name=‘AAA‘;
 
        ID    ID_NAME    ID_NAME NAME
---------- ---------- ---------- ----------
         1         10         10 AAA
 
 
----------------------------------------------------- with as ---------------------------------------------------------------
select * from ( 
   with a as (select 1 from dual union all select 1 from dual), 
         b as (select renshu from (select rownum renshu from a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a) where renshu>30000 and renshu<100000 ) 
   select distinct DECODE(mod(renshu, 3), 
                          1, 
                          decode(mod(renshu, 5), 
                                 2, 
                                 decode(mod(renshu, 7), 
                                        4, 
                                        decode(mod(renshu, 11), 
                                               6, 
                                               decode(mod(renshu, 23), 8, renshu))))) renshu1 
   from b 
) aa where aa.renshu1 is not null; 
 
 
 
-----------------------------------------------------分析函数 ---------------------------------------------------------------
分析函数的功能:排名
1、按照3种方式为cnt列排名
SQL> with t as
  2   (select rownum * 10 cnt  from dual connect by rownum < 5
  3    union all
  4    select rownum * 40 - 10 from dual connect by rownum < 3)t 表构造出10,20,30,30,40,70
  5  select cnt,
  6         row_number() over(order by cnt) rn,//排名无并列,且每个排名与紧接着的下一个排名都是连续的row_number(),不需要参数
  7         rank() over(order by cnt) rk,//排名有并列,且并列的排名与紧接着的下一个排名不连续rank()
  8         dense_rank() over(order by cnt) drk//排名有并列,且并列的排名与紧接着的下一个排名连续dense_rank()
  9    from t;
 
       CNT         RN         RK        DRK
---------- ---------- ---------- ----------
        10          1          1          1
        20          2          2          2
        30          3          3          3
        30          4          3          3
        40          5          5          4
        70          6          6          5
 
已选择6行。
 
2、分组排名:按照dept分组后,按照sale用3种方式排名
SQL> select dept_id,
  2         sale_date,
  3         goods_type,
  4         sale_cnt,
  5         row_number() over(partition by dept_id order by sale_cnt desc) rn,//分组的话,使用partition by dept,排名用order by sale
  6         rank()            over(partition by dept_id order by sale_cnt desc) rk,//partition、order by均可以有多列
  7         dense_rank() over(partition by dept_id order by sale_cnt desc) drk
  8    from lw_sales
  9   where trunc(sale_date, ‘MM‘) = date ‘2013-04-01‘ ;
 
DEPT_I SALE_DATE      GOOD   SALE_CNT         RN         RK        DRK
------ -------------- ---- ---------- ---------- ---------- ----------
S00    18-4月 -13     G01         300          1          1          1
S01    20-4月 -13     G04         900          1          1          1
S01    13-4月 -13     G00         900          2          1          1
S01    15-4月 -13     G01         400          3          3          2
S01    07-4月 -13     G02         300          4          4          3
S01    03-4月 -13     G03         200          5          5          4
S02    05-4月 -13     G03         800          1          1          1
S02    05-4月 -13     G00         400          2          2          2
S02    22-4月 -13     G03         300          3          3          3
S02    06-4月 -13     G04         300          4          3          3
 
已选择10行。
 
 
分析函数的功能:相邻
–LAG   是取到排序后,向上相邻的记录
–LEAD 是取到排序后,向下相邻的记录
LAG/LEAD(v, n, dv) over(partition by a order by b):按照a分组后,每组按b排序,列出排序后v列前n行的值,没有找到则列出dv
例:先按deptno分组,再按sal排序,列出与ename相邻2行的ename,如果往上2行无数据为‘AAA‘,往下2行无数据为‘ZZZ’:
SQL> select deptno,
  2         ename,
  3         sal,
  4         lag(ename, 2, ‘AAA‘)  over(partition by deptno order by sal) lower_name,
  5         lead(ename, 2, ‘ZZZ‘) over(partition by deptno order by sal) higher_name
  6    from scott.emp;
 
    DEPTNO ENAME             SAL LOWER_NAME HIGHER_NAM
---------- ---------- ---------- ---------- ----------
        10 MILLER           1300 AAA        KING
        10 CLARK            2450 AAA        ZZZ
        10 KING             5000 MILLER     ZZZ
        20 SMITH             800 AAA        FORD
        20 JONES            2975 AAA        ZZZ
        20 FORD             3000 SMITH      ZZZ
        30 JAMES             950 AAA        WARD
        30 MARTIN           1250 AAA        TURNER
        30 WARD             1250 JAMES      ALLEN
        30 TURNER           1500 MARTIN     BLAKE
        30 ALLEN            1600 WARD       ZZZ
 
    DEPTNO ENAME             SAL LOWER_NAME HIGHER_NAM
---------- ---------- ---------- ---------- ----------
        30 BLAKE            2850 TURNER     ZZZ
           LiKun                 AAA        ZZZ
 
已选择13行。
 
同样可实现相同的行,只输出一次,如上例中10只输出一次
SQL> select (case
  2           when deptno = lag(deptno, 1, -1) over(partition by deptno order by ename) then//如果deptno=上一行的deptno,则输出null;
  3            null
  4           else//如果不等于,则表示是新数据,直接显示deptno
  5            deptno
  6         end) deptno,
  7         ename,
  8         sal,
  9         lag(ename, 2, ‘AAA‘) over(partition by deptno order by sal) lower_name,
 10         lead(ename, 2, ‘ZZZ‘) over(partition by deptno order by sal) higher_name
 11    from scott.emp;
 
    DEPTNO ENAME             SAL LOWER_NAME HIGHER_NAM
---------- ---------- ---------- ---------- ----------
        10 CLARK            2450 AAA        ZZZ
           KING             5000 MILLER     ZZZ
           MILLER           1300 AAA        KING
        20 FORD             3000 SMITH      ZZZ
           JONES            2975 AAA        ZZZ
           SMITH             800 AAA        FORD
        30 ALLEN            1600 WARD       ZZZ
           BLAKE            2850 TURNER     ZZZ
           JAMES             950 AAA        WARD
           MARTIN           1250 AAA        TURNER
           TURNER           1500 MARTIN     BLAKE
           WARD             1250 JAMES      ALLEN
           LiKun                 AAA        ZZZ
 
已选择13行。
 
 
 
分析函数的功能:统计
 
sum(cnt) over(partition by a order by b) 按a分组后,按b的顺序对cnt进行累计
例如:求出每个部门按商品类型分共累计多少销售额,以及部门中按商品类型累计的销售额
SQL> with t as
  2   (select dept_id,
  3           goods_type,
  4           sum(sale_cnt) goods_sale_cnt
  5      from lw_sales
  6     group by dept_id, goods_type)
  7  select dept_id,
  8         goods_type,
  9         goods_sale_cnt,
 10         sum(goods_sale_cnt) over(partition by dept_id order by goods_type) cum_goods_sale_cnt
 11    from t;
 
DEPT_I GOOD GOODS_SALE_CNT CUM_GOODS_SALE_CNT
------ ---- -------------- ------------------
S00    G00             400                400
S00    G01             330                730
S00    G03            1000               1730
S00    G04            1000               2730
S00    G05             900               3630
S01    G00            1600               1600
S01    G01             800               2400
S01    G02            1400               3800
S01    G03             800               4600
S01    G04            2530               7130
S02    G00             400                400
S02    G01             270                670
S02    G02             900               1570
S02    G03            1100               2670
S02    G04             300               2970
S02    G05             200               3170
 
已选择16行。
 
avg() over()平均值
例如:求出每个部门每种货物的累计销售额,以及这种货物在各部门中的平均销售额,以及他们的差
SQL> with t as
  2   (select dept_id, goods_type, sum(sale_cnt) goods_sale_cnt
  3      from lw_sales
  4     group by dept_id, goods_type)
  5  select dept_id,
  6         goods_type,
  7         goods_sale_cnt,
  8         round(AVG(goods_sale_cnt) over(partition by goods_type), 2) avg_goods_sale_cnt,
  9         goods_sale_cnt -
 10         round(AVG(goods_sale_cnt) over(partition by goods_type), 2) dv_goods_sale_cnt
 11    from t;
 
DEPT_I GOOD GOODS_SALE_CNT AVG_GOODS_SALE_CNT DV_GOODS_SALE_CNT
------ ---- -------------- ------------------ -----------------
S00    G00             400                800              -400
S01    G00            1600                800               800
S02    G00             400                800              -400
S00    G01             330             466.67           -136.67
S01    G01             800             466.67            333.33
S02    G01             270             466.67           -196.67
S01    G02            1400               1150               250
S02    G02             900               1150              -250
S00    G03            1000             966.67             33.33
S01    G03             800             966.67           -166.67
S02    G03            1100             966.67            133.33
S00    G04            1000            1276.67           -276.67
S01    G04            2530            1276.67           1253.33
S02    G04             300            1276.67           -976.67
S00    G05             900                550               350
S02    G05             200                550              -350
 
已选择16行。
 
max(col1) over(partition by col2 order by col3):partition可选;order by可选,写上表示依次选出最大,不写表示组内所有值的最大
按月分组列出销售额的最高和最低
SQL> select dept_id,
  2         to_char(sale_date, ‘YYYY-MM‘) sale_month,
  3         sum(sale_cnt) goods_sale_cnt,
  4         max(sum(sale_cnt)) over(partition by to_char(sale_date, ‘YYYY-MM‘)) max_gsc,
  5         min(sum(sale_cnt)) over(partition by to_char(sale_date, ‘YYYY-MM‘)) min_gsc
  6    from lw_sales
  7   where goods_type = ‘G01‘
  8   group by dept_id, to_char(sale_date, ‘YYYY-MM‘)
  9  ;
 
DEPT_I SALE_MO GOODS_SALE_CNT    MAX_GSC    MIN_GSC
------ ------- -------------- ---------- ----------
S00    2013-03             30        400         30
S01    2013-03            400        400         30
S02    2013-03            270        400         30
S00    2013-04            300        400        300
S01    2013-04            400        400        300
 
 
 
 
 
 
 
-----------------------------------------------------函数索引 ---------------------------------------------------------------
SQL> select job,count(distinct deptno) from scott.emp where mgr is not null group by job;
 
JOB       COUNT(DISTINCTDEPTNO)
--------- ---------------------
CLERK                         3
SALESMAN                      1
MANAGER                       3
ANALYST                       1
 
SQL> set autot trace
SQL> select job,count(distinct deptno) from scott.emp where mgr is not null group by job;
 
 
执行计划
----------------------------------------------------------
Plan hash value: 3818262728
 
----------------------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |     5 |   105 |     4  (25)| 00:00:01 |
|   1 |  HASH GROUP BY       |           |     5 |   105 |     4  (25)| 00:00:01 |
|   2 |   VIEW               | VM_NWVW_1 |    11 |   231 |     4  (25)| 00:00:01 |
|   3 |    HASH GROUP BY     |           |    11 |   165 |     4  (25)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL| EMP       |    11 |   165 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - filter("MGR" IS NOT NULL)
 
 
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        578  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed
 
SQL> select job,count(distinct deptno) from scott.emp where nvl2(mgr,1,0)=1 group by job order by job;
 
 
执行计划
----------------------------------------------------------
Plan hash value: 2809461788
 
----------------------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |     1 |    21 |     4  (25)| 00:00:01 |
|   1 |  SORT GROUP BY       |           |     1 |    21 |     4  (25)| 00:00:01 |
|   2 |   VIEW               | VM_NWVW_1 |     1 |    21 |     4  (25)| 00:00:01 |
|   3 |    HASH GROUP BY     |           |     1 |    15 |     4  (25)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL| EMP       |     1 |    15 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - filter(NVL2("MGR",1,0)=1)
 
 
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        575  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          4  rows processed
 
SQL> create index i_emp_mgr on scott.emp(nvl2(mgr,1,0));
 
索引已创建。
 
SQL> select job,count(distinct deptno) from scott.emp where nvl2(mgr,1,0)=1 group by job order by job;
 
 
执行计划
----------------------------------------------------------
Plan hash value: 1097451957
 
--------------------------------------------------------------------------------------------
| Id  | Operation                      | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |           |     1 |    21 |     3  (34)| 00:00:01 |
|   1 |  SORT GROUP BY                 |           |     1 |    21 |     3  (34)| 00:00:01 |
|   2 |   VIEW                         | VM_NWVW_1 |     1 |    21 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY               |           |     1 |    14 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| EMP       |     1 |    14 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | I_EMP_MGR |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   5 - access(NVL2("MGR",1,0)=1)
 
 
统计信息
----------------------------------------------------------
          2  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        575  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          4  rows processed
 
 
 
 
-----------------------------------------------------用 EXISTS 替换DISTINCT ---------------------------------------------------------------
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT 子句中使用
DISTINCT. 一般可以考虑用EXIST 替换
例如:
低效:(nestloop,外加unique)
SELECT DISTINCT d.DEPTNO, d.DNAME
  FROM scott.DEPT D, scott.EMP E
 WHERE D.DEPTNO = E.DEPTNO
执行计划
----------------------------------------------------------
Plan hash value: 2401638402
 
----------------------------------------------------------------------------------
| Id  | Operation           | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |            |     5 |    80 |     4  (25)| 00:00:01 |
|   1 |  HASH UNIQUE        |            |     5 |    80 |     4  (25)| 00:00:01 |
|   2 |   NESTED LOOPS      |            |    12 |   192 |     3   (0)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| DEPT       |     4 |    52 |     3   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN | EMP_DEPTNO |     3 |     9 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - access("D"."DEPTNO"="E"."DEPTNO")
       filter("E"."DEPTNO" IS NOT NULL)
高效:(nestloop)
SELECT d.DEPTNO, d.DNAME
  FROM scott.DEPT D
 WHERE EXISTS (SELECT 1 FROM scott.EMP E WHERE E.DEPTNO = D.DEPTNO);
执行计划
----------------------------------------------------------
Plan hash value: 3369102344
 
---------------------------------------------------------------------------------
| Id  | Operation          | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |            |     3 |    48 |     3   (0)| 00:00:01 |
|   1 |  NESTED LOOPS SEMI |            |     3 |    48 |     3   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT       |     4 |    52 |     3   (0)| 00:00:01 |
|*  3 |   INDEX RANGE SCAN | EMP_DEPTNO |     8 |    24 |     0   (0)| 00:00:01 |
---------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("E"."DEPTNO"="D"."DEPTNO")
       filter("E"."DEPTNO" IS NOT NULL)
 
EXISTS 使查询更为迅速,因为RDBMS 核心模块将在子查询的条件一旦满足后,立刻返回结
果.
 
-----------------------------------------------------------索引合并 ---------------------------------------------------------------
empno和ename上都有unique索引,会根据2个索引的rowid做个bitmap合并
 
SQL> select * from scott.emp e where e.empno=7369 and ename=‘SMITH‘;
 执行计划
----------------------------------------------------------
Plan hash value: 2575831182
 
-------------------------------------------------------------------------------------------
| Id  | Operation                        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |        |     1 |    39 |     0   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID     | EMP    |     1 |    39 |     0   (0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS    |        |       |       |            |          |
|   3 |    BITMAP AND                    |        |       |       |            |          |
|   4 |     BITMAP CONVERSION FROM ROWIDS|        |       |       |            |          |
|*  5 |      INDEX RANGE SCAN            | PK_EMP |     1 |       |     0   (0)| 00:00:01 |
|   6 |     BITMAP CONVERSION FROM ROWIDS|        |       |       |            |          |
|*  7 |      INDEX RANGE SCAN            | BBB    |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   5 - access("E"."EMPNO"=7369)
   7 - access("ENAME"=‘SMITH‘)
 
-----------------------------------------------------------索引优先级 ---------------------------------------------------------------
empno上有唯一索引,deptno上为非唯一索引,但是查询选择使用depno上的索引,
虽然 EMPNO 是唯一性索引,但是由于它所做的是范围比较, 等级要比非唯一性索引的等式比较低!
SQL> select * from scott.emp e where e.empno>73 and deptno=20;
执行计划
----------------------------------------------------------
Plan hash value: 1182541070
 
------------------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |     3 |   117 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP        |     3 |   117 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | EMP_DEPTNO |     3 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("E"."EMPNO">73)
   2 - access("DEPTNO"=20)
 
 
-----------------------------------------------------------Order by 使用索引 ---------------------------------------------------------------
ORDER BY 子句只在两种严格的条件下使用索引.
1ORDER BY 中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.
2ORDER BY 中所有的列必须定义为非空.
 
SQL>  select * from test1  order by id1;(未使用索引,因为列不是非空)
 
已选择8192行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 1692556001
 
------------------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  8192 |   416K|       |   125   (1)| 00:00:02 |
|   1 |  SORT ORDER BY     |       |  8192 |   416K|   528K|   125   (1)| 00:00:02 |
|   2 |   TABLE ACCESS FULL| TEST1 |  8192 |   416K|       |    17   (0)| 00:00:01 |
------------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
 
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         58  consistent gets
          0  physical reads
          0  redo size
     176097  bytes sent via SQL*Net to client
       6422  bytes received via SQL*Net from client
        548  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       8192  rows processed
 
SQL> alter table test1 modify id1 not null;
 
表已更改。
 
SQL>  select * from test1  order by id1;(走索引)
 
已选择8192行。
 
 
执行计划
----------------------------------------------------------
Plan hash value: 2136352608
 
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |  8192 |   416K|    32   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST1 |  8192 |   416K|    32   (0)| 00:00:01 |
|   2 |   INDEX FULL SCAN           | MMM   |  8192 |       |    19   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
 
统计信息
----------------------------------------------------------
         30  recursive calls
          0  db block gets
      13526  consistent gets
         17  physical reads
          0  redo size
     248747  bytes sent via SQL*Net to client
       6422  bytes received via SQL*Net from client
        548  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
       8192  rows processed
 
 
-----------------------------------------------------------NOT in 与 NOT exists --------------------------------------------------------------
exist不检测实际的值~只是做一个存在判断就立刻返回~所以null值也返回给结果集了
in 会查询结果集除了null值不会返回以外还会过滤重复的值
 
这两个sql不等价:
SQL> SELECT count(*) FROM qdata.t_pub010 t1 WHERE t1.f006 NOT IN (SELECT t2.comcode FROM qdata.t_stk005 t2);
 
  COUNT(*)
----------
      8725
SQL> SELECT count(*) FROM qdata.t_pub010 t1 WHERE NOT exists (SELECT t2.comcode FROM qdata.t_stk005 t2 where t2.comcode=t1.f006) ;
 
  COUNT(*)
----------
     22092
 
跟这个才等价:
SQL> SELECT count(*) FROM qdata.t_pub010 t1 WHERE NOT exists (SELECT t2.comcode FROM qdata.t_stk005 t2 where t2.comcode=t1.f006) and t1.f006 is not null;
  COUNT(*)
----------
      8725
 
简单说就是not in不会统计结果为null的,而not exists只是逻辑判断,所以包含为null的结果:
SQL> select count(*) from scott.emp where comm not in (1000,300);
 
  COUNT(*)
----------
         3
 
 
 
-----------------------------------------------------------标量子查询的等价 --------------------------------------------------------------
DROP TABLE TEST111;
CREATE TABLE TEST111 (ID INT );
INSERT  INTO TEST111 VALUES (1);
INSERT  INTO TEST111 VALUES (2);
INSERT  INTO TEST111 VALUES (3);
COMMIT;
DROP TABLE TEST222;
CREATE TABLE TEST222 (ID INT );
INSERT  INTO TEST222 VALUES (1);
INSERT  INTO TEST222 VALUES (2);
INSERT  INTO TEST222 VALUES (NULL);
COMMIT;
 
 
如下两种写法等价
SELECT T1.ID  ,   (SELECT T2.ID FROM TEST222 T2 WHERE ID=T1.ID)             FROM TEST111 T1;
 
 
                                     ID (SELECTT2.IDFROMTEST222T2WHERE
--------------------------------------- ------------------------------
                                      1                              1
                                      2                              2
                                      3 
                                      
                                      
 
 
SQL> SELECT T1.ID ,T2.ID FROM TEST111 T1 ,TEST222 T2 WHERE T1.ID=T2.ID(+);
 
                                     ID                                      ID
--------------------------------------- ---------------------------------------
                                      1                                       1
                                      2                                       2
                                      3 
 
如果不写成外连接,将不等价
 
SQL> SELECT T1.ID ,T2.ID FROM TEST111 T1 ,TEST222 T2 WHERE T1.ID=T2.ID;
 
                                     ID                                      ID
--------------------------------------- ---------------------------------------
                                      1                                       1
                                      2                                       2
 
 
如下是等价的 :
 
 
select a.username, count(owner)
  from all_users a, all_objects b
where a.username = b.owner (+)
group by a.username;
 
select a.username,
      (select count(*) from all_objects b where b.owner = a.username) cnt
from all_users a
 
 
-----------------------------------------------------------行列转换函数 --------------------------------------------------------------
LISTAGG,多行合并,11.2新特性
SQL> select listagg(comm,‘,‘) within group(order by empno) from scott.emp;
 
LISTAGG(COMM,‘,‘)WITHINGROUP(ORDERBYEMPNO)
--------------------------------------------------------------------------
1000,300,500,1400,0
说明:合并会忽略空值,不能去重,如果想去重,需要在from中用子查询distinct,如:根据deptno分组,把job合并。
SQL>select deptno,listagg(job,‘,‘) within group(order by job) list from (select distinct deptno,job from scott.emp) group by deptno;//去重后
    DEPTNO LIST
---------- ------------------------------
        10 CLERK,MANAGER,PRESIDENT
        20 ANALYST,CLERK,MANAGER
        30 CLERK,MANAGER,SALESMAN
 
SQL> select deptno,listagg(job,‘,‘) within group(order by job) list from scott.emp group by deptno;//未去重
    DEPTNO LIST
---------- ------------------------------
        10 CLERK,MANAGER,PRESIDENT
        20 ANALYST,CLERK,MANAGER
        30 CLERK,MANAGER,SALESMAN,SALESMAN,SALESMAN,SALESMAN
 
WM_CONCAT
SQL> select deptno,wmsys.wm_concat(job) from emp group by deptno;
    DEPTNOWMSYS.WM_CONCAT(JOB)
------------------------------------------------------------------------------------------
        10MANAGER,CLERK,PRESIDENT
        20CLERK,ANALYST,CLERK,ANALYST,MANAGER
        30SALESMAN,CLERK,SALESMAN,MANAGER,SALESMAN,SALESMAN
SQL> select deptno,wmsys.wm_concat(distinct job) from emp group by deptno;
    DEPTNOWMSYS.WM_CONCAT(JOB)
------------------------------------------------------------------------------------------
        10MANAGER,CLERK,PRESIDENT
        20ANALYST,CLERK,MANAGER
        30SALESMAN,CLERK,MANAGER
说明:合并会去重,10g以后有,不能排序,可以去重。未公开函数,10g返回值是varchar,11.2.0.3.2变为clob。可以用做分析函数
SQL> select deptno,to_char(wmsys.wm_concat(job) over(partition by deptno order by job)) a from scott.emp;
    DEPTNO A
---------- ------------------------------------------------------------
        10 CLERK
        10 CLERK,MANAGER
        10 CLERK,MANAGER,PRESIDENT
        20 ANALYST
        20 ANALYST,CLERK
        20 ANALYST,CLERK,MANAGER
        30 CLERK
        30 CLERK,MANAGER
        30 CLERK,MANAGER,SALESMAN,SALESMAN,SALESMAN,SALESMAN
        30 CLERK,MANAGER,SALESMAN,SALESMAN,SALESMAN,SALESMAN
        30 CLERK,MANAGER,SALESMAN,SALESMAN,SALESMAN,SALESMAN
 
 
-----------------------------------------------------------in 子查询改写 --------------------------------------------------------------
in的子查询可以成表关联方式,但是要注意去重
去重可通过group by或者distinct,否则会出现重复值
SQL> select * from az1;
 
         N
----------
         1
 
SQL> select * from az2;
 
         N
----------
         1
         1
         1
         1
 
SQL> select * from az1 where n in (select n from az2);
 
         N
----------
         1
 
SQL> select * from az1,az2 where az1.n=az2.n group by az1.n,az2.n;
 
         N          N
---------- ----------
         1          1
 
SQL> select distinct * from az1,az2 where az1.n=az2.n;
 
         N          N
---------- ----------
         1          1
 
-----------------------------------------------------------根据某字段更新的优化 --------------------------------------------------------------
aaa表和bbb表是从scott.emp创建,至13000行,并把empno改成rownum;
 
原始语句:
 update bbb set sal=(select sal from aaa where bbb.empno=aaa.empno) where bbb.job=‘PRESIDENT‘;
35S
 
优化为游标方式:
create or replace procedure fuck is
  cursor allamt is
    select a.sal, a.empno from aaa a, bbb b where a.empno = b.empno;
begin
  FOR tc in allamt loop
    update bbb b set b.sal = tc.sal where b.empno = tc.empno and b.job=‘PRESIDENT‘;  
  end loop;
  commit;
end fuck;
/
exec fuck;
13s
 
再次优化为merge:
merge into bbb
using aaa
on (aaa.empno=bbb.empno)
   when matched then
        update set bbb.sal=aaa.sal , bbb.mgr=aaa.mgr
where bbb.job=‘PRESIDENT‘;
2S
 
 
 
-----------------------------------------------------------insert all --------------------------------------------------------------
insert all
into a
into b
select * from dba_objects;
同时插入2张表,一个事务。一份复制,2份粘贴。性能更好。
分成2条写,有可能中间dba_objects数据有了变化,导致ab表不一致了。
 
 
-----------------------------------------------------------connect by实现行展开成列 --------------------------------------------------------------
WITH T AS (
  SELECT ‘a‘ name ,1 ST, 4 EN FROM DUAL UNION ALL
  SELECT ‘b‘ name ,1 ST, 4 EN FROM DUAL UNION ALL
  SELECT ‘D‘ name ,3 ST, 3 EN FROM DUAL UNION ALL
  SELECT ‘C‘ name ,7 ST, 9 EN FROM DUAL
  )
  SELECT T.name name
         ,ROW_NUMBER()OVER(PARTITION BY T.name ORDER BY 1)+T.ST-1 RN
  FROM T
  CONNECT BY LEVEL <= (T.EN-T.ST+1) AND T.name = PRIOR T.name AND PRIOR SYS_GUID() IS NOT NULL ;



sql优化点整理