单表优化是sql优化中最基础的部分,单表可以理解为复杂查询中的单表部分, 熟悉单表优化,应用于实际的sql优化中,解决sql效率低的问题。

一、基础知识

   

1、操作符

 执行计划中的每一个节点,每一个节点都是一个循环获取数据的过程。

单表优化中常见有NSET,PRJT2,CSCN2,SSEK,CSEK,SSCN等


   


 2、hint

客户端将sql语句提交给服务器后,优化器根据自己的算法,基于统计信息 生成一条认为代价最低的执行计划,但是有时计算出来的执行计划并不是最优的,我们可以根据实际情况,给优化器提示,让优化器根据提示修正执行计划。

3、提前返回

执行一条语句的时候,当得出的结果 满足FIRST_ROWS 设置的值时,语句就停止执行,可以向客户端发出执行完成,返回数据。其实这个时间语句并没真正执行完成,这就叫做提前返回数据。在一些场景下 并不是要全部的数据,提前返回能尽快返回数据,提高体验感。

   4、查询的基本结构

     select id,mc from TAB where id<9999 order by id desc;

这条查询语句 select 后 为查询项,from 后为查询的表,where 后为条件列 ,order by 为排序或者分组模块。

二、单表优化

1、等值查询

等值查询为最基础的查询

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_执行计划

上图无索引的时候 是全表扫描,下图新建条件列索引后走了索引扫描

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_数据库_02

但是也需要注意:

条件的列过滤性是否比较好的,如果过滤性较差,查出的结果比较大还需要回表可能会比全表扫描更差。

传的值是否能转换成列的类型

条件列套函数的时候,FUNC(OBJECT_ID)=xxx的时候,内部函数 可以 考虑做函数索引,但也有可能用的自定义函数,自定义函数为确定性函数才可。

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_操作符_03

2、IN条件查询

   In条件的查询 可以理解为多个等值条件,有两种处理

1.in的值作为参数表,将传入的3值分别和表连接。列有索引并且过滤性好的话 走的nest loop,否则将会走hash join

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_数据库_04

  2.将in 作为一个整体条件处理。 IN 里面如果参数非常多,解析会非常耗时,影响性能。

3、范围条件

在条件列增加索引可达到优化效果,当然也要注意其过滤性。

范围的条件 会根据统计信息估算 大概返回多少行来选定 SSEK2 或者SSCN 的扫描方式。

4、like条件

1.传值接%号的时候,如like fff||”%” 优化器会自动转换为 大于 fff 并且小于xxx 这样的查询方式。

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_操作符_05

2.百分号加传值的时候,如lik ‘%’||fff ,这种情况下,可以通过两次反转 变成传值在前 而走函数索引。如 字段内容  AAAAAfff 是符合like ‘%’||fff的。通过函数 reverse 将字段的值反转过来为fffAAAAA, like 条件也反转like fff||”%” 最终like的 结果为一致的, 因此百分号加传值的时候,可以用reverse(c1) 并建立函数索引,把like反转来优化。

但是需要注意的是 reverse函数不忽略末尾空格,但是like 是考虑空格的。

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_sql_06

ID  MC

11223344   yyxx

33311223344   yyxx

dsr3344    yyxx

 通过反转函数下面这图 查询出来少了一行,因为 dsr3344 后面有空格。

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_执行计划_07

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_数据库_08

创建了 reverse(ID) 索引后,改写%在后面,成了 索引扫描

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_操作符_09

3. 前后% 号的 like 如  like ‘%3344%’ ,一般前后%号的like 无法优化的,但是3344为固定值的话可以转换成 POSITION函数,存在POSITION(‘3344’,ID)索引 就会走索引定位

达梦将timestamp with time zone 转换成了long 达梦时间处理函数_数据_10

如果 like’%中文%’ 可以考虑全文索引。

5、排序

最简单的 排序 字段 加所以 即可消除排序。排序的操作符为 SORT 。有排序的弊端为不能 提前返回数据了, 必须要扫描完成 排序后才可以返回。 排序列有索引的情况 直接扫描索引,满足行数后即可返回数据。

正常情况下的查询 是where 条件等于 后加某个字段排序, 这时可以加组合索引, 先定位where 条件的列,组合排序的列。

Id > 1000 order by mc  这种情况下 要考虑条件过滤的多少数据,如果过滤后还有80% 大量数据的话可以考试 在mc 上建立索引,返回有序数据后再select.

如果返回数据量少 可以 建立 ID 和mc 的组合索引。

6、分组、分析函数

分组和排序类似,分组列在有索引的情况下,可以 通过扫描索引减少计算。

分析函数是按照OVER 后的PARTION 和ORDER 进行数据输出,对于同一个分组内的数据进行排序列排序

这个时候我们需要 PARTION BY 列 + ORDER 列的组合索引

7、多条件组合

都是等值的情况下,可以建立组合索引,要考虑过滤性。

有等值和范围的时候, 考虑过滤性后 建立等值索引在前 范围索引后的组合索引,范围索引只能用一个。

都是范围的情况, 只能取一个过滤性好的建立索引。

建立索引 考虑过滤性,定位少量数据后 可以slct 选择数据。

8、多条件组合 OR

HINT OPTIMIZER_OR_NBEXP 通过该hint 优化。 0 为分开,2为合并处理。

Where 的条件前面为公共部分, or 的是分支,当公共部分很复杂的时候,可以考虑公共部分作为一个临时表, OR_CVT_HTAB_FLAG 0表示公共部分不转换为临时表,1表示转换 。USE_HTAB_FLAG  表示是否开启临时表功能,为0表示强制不使用

Or 非常多的情况,优化器more 处理7个分支,MAX_OPT_N_OR_BEXPS 可以调大处理的分支

9、层次查询

connect by col1 = prior.col2  start with col4 = v4 where col3 = v3;

的时候,v3 是不能提前处理的, 此时建立col1 索引,每次connect by都能快速定位。

from (select * from t1 where col3 = v3) connect by col1 = prior.col2 start with col4 = v4

Where col3 = v3; 这样查询一个子集的时候,v3会被优先处理,可以加上col3和col1 的组合索引。

对于CONNECT BY 子集比较多且无法建立索引或者过滤不佳的情况下,可以考虑将CNNTB_OPT_FLAG

HINT 成1,通过HASH 的方式进行层次查询操作

三、常用监控

1、ET的方法

会记录下各个操作符的执行次数及执行时间, 尽可能的只开启会话级参数

CALL SF_SET_SESSION_PARA_VALUE(’MONITOR_SQL_EXEC’,1);

CALL SF_SET_SESSION_PARA_VALUE(‘MONITOR_TIME’,1);

开启后 ET(执行号) 获取执行情况。

2、AUTOTRACE

这个是disql 的功能,开启的命令在DISQL中执行set autotrace trace,然后再执行语句,在开启MONITOR_SQL_EXEC的情况下,处理记录下各个操作符的执行

信息,还会记录下相关的行数信息,对比评估和实际返回的行数,差距大的部分可能执行计划有问题。

3、PLNDUMP

用来dump 真正的执行计划,判断内存的执行计划和 看到的是否有差别。

select cache_item,sqlstr from v$cachepln where sqlstr like ‘%where%’;

查询确认CACHE_ITEM号后 dump内存里的执行计划

Alter session set events  ’immediate trace name plndump,level 214587956’;

即可获得该计划的详细信息,存放在数据文件目录的trace文件夹下