sql优化最佳实践记录

一、理论
1.优化器根据各种判断条件,将提交的sql进行优化,提交给执行器执行,sql执行过程在这里插入图片描述
2.优化器的种类cro为基于规则的优化器不怎么用, cbo基于成本的优化器,通常都会用cbo,成本越低执行效率越高
优化器模式 optimizer_mode,allow rows cbo优化器的第一种具体优化方法,以数据吞吐量为目标,以最少资源完成语句,first rows,优化器使用的第二种具体优化方法,以响应时间为目标,以最短的时间响应返回数据的前几行,10g以后不再支持rbo,mode的最新默认值为allow rows
3.参数optimizer feature enable控制使用的优化器版本,版本不同特征不同,不同特征表现可能选择的执行路径就不同
4.除了设置版本号,还可以使用hint提示干预优化器
格式是 explain plan for select /+ rule/ fsubject from t_todo_todo
不同的hint显式改变优化器的配置,更加灵活
5.执行计划中成本的计算读取单数据块的时间+读取多数据块的时间+cpu操作的时间总和除以读取单数据块的平均时间即成本=io+cpu,其中io的计算是单数据块的读取次数*单次读取的平均时间+多数据块的读取次数平均时间
6.执行计划,数据库执行sql是分步骤执行的,由优化器决定的最佳执行路径为执行计划
7.执行计划解释 select statement表示语句类型为一条select,而非真正操作,又是也会省略,如果是update 则为update statement,其他相同;Predicate Information谓词执行计划中id打星号证明该步操作有相关的谓词操作,即查询或者过滤条件access表示访问条件,即通过某条件定位到某数据
8.库执行计划存储方式生成执行计划开销很大,在oracle内存中共享池中有一部分是库高速缓存,一般一条sql生成的执行计划会缓存起来,下次同样的sql直接执行缓存中的执行计划,每条sql都有一个sql_id标识,且占一定的内存空间,该内存中一部分是与sql语句相关称为父游标,与执行计划相关的称为子游标,子游标会不同,比如同一条sql不同用户执行,就会有不同的子游标
9.执行计划执行顺序,从上到下,从左到右寻找直到找到某个节点没有子节点则执行,然后再依次执行同级的下一个节点,下一个节点有子节点则同理执行,最后汇总到第一节点,下图中执行顺序为24531
在这里插入图片描述
10.执行计划中operation列访问路径的分类
在这里插入图片描述
11.查看执行计划方法
select /
+ALL_rows*/ * from table(dbms_xplan.display)
–查看执行计划方法2(前面是oracle 的工作目录必须在命令窗口执行)
@E:\oracle\RDBMS\ADMIN\utlxplp.sql;
执行计划和查看执行计划必须在同一个会话中进行;
在sqlplus中set autotrace traceonly (常用) 然后直接输入语句
活 explain plan for select … 执行select /+ALL_rows/ * from table(dbms_xplan.display)
12.执行计划表中主要字段的说明cost值在计划中这一步所消耗的相对资源,card为计划中执行这一步所处理的行数,bytes为这一步处理的所有记录的字节数预计值,rowid(伪列)与每行数据的物理位置挂钩,即使发生行迁移也不变;recursive calls为执行slq语句时隐性额外发出的sql语句,如触发器;row source行源,上一步操作返回的行集合;predicate 谓词,一个查询中where限制条件;driving table驱动表在哈希和嵌套连接当中,在应用完查询条件之后,能够返回较少行源的表作为驱动表,返回的行数越多对后续操作影响越大;probed table 被探视表也叫内层表,在驱动表中返回一条数据后会在该表中寻找符合连接条件的行,一般为返回行源较大的表,相应的连接列上应该有索引;concatenated index组合索引,多列构成的索引,eg:create index inx on emp(col1,col2) col为引导列, 当where条件中保扩col=?时才会使得改索引生效:selectivity可选择性,列中不重复记录/该列总数,越接近1,越适合加索引,且返回的行源越少
13.查看执行计划autotrace的几种配置
1.该命令只能在sqlplus中用,默认set autotrace off,只显示执行结果; set autotrace on即显示执行结果也显示统计信息同时显示执行计划,缺点是得先执行,增加了查看时间;set autotrace on explain,只显示执行结果和执行计划,不显示统计结果
14. 关于autotrace中的统计结果字段的讲解 ,https://www.cnblogs.com/nathon-wang/p/10754595.html , https://blog.csdn.net/sanshou/article/details/18699825 ,set autotrace only就是跟on一样的作用,表示只显示统计信息db block gets表示缓存中数据块被修改的个数,consistent gets缓存逻辑读,读取块的次数,physical read物理读,从物理位置读到缓存区的数据块数目,
它们三者之间的关系大致可概括为
逻辑读指的是Oracle从内存读到的数据块数量。一般来说是’consistent gets’ + ‘db block gets’。当在内存中找不到所需的数据块的话就需要从磁盘中获取,于是就产生了’physical reads’。
Physical Reads通常是我们最关心的,如果这个值很高,说明要从磁盘请求大量的数据到Buffer Cache里,通常意味着系统里存在大量全表扫描的SQL语句,这会影响到数据库的性能,因此尽量避免语句做全表扫描,对于全表扫描的SQL语句,建议增 加相关的索引,优化SQL语句来解决。
关于physical reads ,db block gets 和consistent gets这三个参数之间有一个换算公式:
数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。 (公式可能不太准确,但是增减关系是正确的)https://www.cnblogs.com/xqzt/p/4467867.html
15.高水位线的概念,主要用于区分数据段中已使用的数据块和未使用的数据块 讲解 https://www.cnblogs.com/husam/p/6604437.html ,delete操作的删除不会影响高水位线HWM,但是truncate则会清除高速缓存,再次全表扫描逻辑读和物理读字节数都变少,高水位线也会降到最低,即truncate会降低高水位线
;高水位线以下表示已使用,以上表示未使用,插入数据会让高水位线上移,但是删除不会下移;全表扫描就是从段头扫描到高水位线,即使什么也没有,高水位线的信息存储于段中的第一个块,也就是真正存储数据的是从第2块开始的
15.1高水位线的查看统计
Set serveroutput on 可能需要
exec show_space(tablename,username),高水位线=total blocks-unused blocks,全表扫描的逻辑读和物理读都是到高水位线
16统计信息,统计oracle中的对象或者系统本身的统计表,cbo正是基于统计信息来分析执行计划的
17统计信息分类,分为系统统计信息,对象统计信息,数据字典统计信息
18.表索引的统计信息
select* from user_indexes where table_name=’’,
blevel表示btree的深度,从root节点到level节点的深度;clustering_factor聚簇因子:参考 https://blog.csdn.net/tlx20093a/article/details/7683528 。
19.聚簇因子是指通过一个索引全表扫描时需要访问的表的数据块的数量,oracle中虽然逻辑上是一行一行数据进行访问的,但是物理存储上确实随机的按照数据块存储的,I/O的最小单位也是数据块,如果索引排序的索引行序与对应表中数据行序相似度很高,那么按照索引排序的行rowid查找数据时物理读取数据块会很少,聚簇因子的计算:初始值是1,每读取一个数据块,clustering_factor+1,最好的结果时等于表的数据块数,最坏的结果等于行数;越接近块数,说明索引范围扫描的代价越低,索引效率越高

20.表统计信息,三张表: user_tables a where a.table_name;user_indexes a where a.table_name;user_tab_cols
21.user_tab_cols中列的一些解释:num_distinct列中不同值的统计,num_nulls列中空值的统计,density选择率,histo是否有直方图num_buckets直方图的桶数
22.选择率反映了字段的选择性,没有直方图的列,选择率为1/num_distinct,
23.histogram直方图三种类型,none表示没有,frequency频率类型,height balance平均分布类型
24.直方图对执行计划的影响:如果数据字段存在倾斜(比如owner=1 数据为1条,owner=2数据为1w条)则有直方图时计算成本的基数为num_rowsdensity 没有直方图则为num_rows/num_distinct,成本计算不同,执行计划不同
25.sql解析过程,语法检查,语义检查(权限等),共享池检查,将sql文本解析成hash值,就是sql视图中的sqlid,检查共享池中是否有,如果共享池中有sql则进行软解析,否则硬解析,然后进行rowsource 优化,生成最优的执行计划,之后 row resouce generation即在执行计划的每一步中会采取什么的方式做,最后执行
在这里插入图片描述
26.
硬解析就是当检查到共享池中没有sql的时候会先生成游标,即父游标个子游标,软解析就是已经有sql时,只判断是否需要生成一个子游标存放执行计划,硬解析尽量避免的原因是执行消耗cpu,另外要生成父子游标,由于游标是共享的,实际操作会同步,降低效率,软解析当生成子游标时也要加锁同步,应该尽量避免
27.不同用户的同名表不能共享执行计划,v&sqlarea这个表中查询的是游标相关信息,version_count字段某个父游标拥有的子游标的数目
28表的类型之堆表,常用的也是默认的表类型,其影响性能的因素主要是表的规模,规模越大,扫描的数据块越多,成本越高,高水位线影响其大小
29 iot索引组织表 参考 https://www.cnblogs.com/nieliu/archive/2012/05/04/2482223.html 索引和数据合二为一,堆表是物理存储无序的,而索引组织表是按照主键顺序排序后存储,本身就是索引,大大降低io,创建方式create table t_iot () organization index
30.分区表,参考 https://blog.csdn.net/u014714841/article/details/84583033 ,物理存储上将表进行内存分区,将一张大表分成物理级别的小表,通过查询条件直接定位到对应的分区表上,大大减少表的扫描次数,分区表分为范围分区,哈希分区等
31.字段影响sql执行效率的两个因素,字段的存储顺序,行记录的存储结构是头记录+字段长度+字段内容,数据库不知道每个字段的偏移量,因此每一个字段都是由第一个字段开始定位的,设计表时应该将经常用到的字段放在靠前的位置;字段类型,错误的字段类型会导致优化器做自动类型转化,从而导致全表扫描,比如字段类型是varchar2,查询值为number,则优化器会做tonumber的转化,导致无法走索引
32.索引:B树索引,整体结构是一个平衡树(左右两个子树的高度差绝对值不大于1的树),一共有三种节点,跟节点,路有节点,叶子节点,每个叶子节点包括两部分记录,分别是索引值和rowid
在这里插入图片描述
33.dblink 数据链对象,一般远程对象优化器无法保证能够产生高效的执行计划
34.表空间在物理上对应一个或多个文件, 是oracle逻辑存储结构
数据段存储物理上可以来自一个或者多个文件,但其中指定的区只能来自于同一个文件,每建立一个基表,会在当前用户默认表空间中建一个数据段,段分类:表段,一张表就有一个段;索引段:保存索引数据:表分区段,分区表分区后,每个区都有段,大对象段:表中如果有大对象,且对象大小超过一定范围,则会将对象单独保存在段中,表中只存放指针;
区是磁盘分配的最小单位当段空间用完,系统会以增加区的方式增加段空间
块是最小的物理单位,大小一般是os物理块的整数倍,也就是512k的倍,块的大小在数据库创建时就确定了,不可修改,块中重要的参数:pctfree 块空闲率,默认10指当插入行时块的剩余空闲达到空闲率时不再允许新增行,空闲的空间主要是为行的更新而准备的;pctused块使用率当块空间低于使用率 块又允许新插入行,主要用于限制插入;initrans最小事务数;maxtrans最大并发事务数
行链接当行首次被插入时,如果一个块无法完全容纳这行,则会在区中找多个块来容纳一行,当行中有long、lob等类型时,行链接无法避免行迁移:更新行时,导致行长增加,而此时已达到pctfree则会将整个行迁移到另外一个大的块中,原来的块中只存放rowid和下一个块的指针,rowid不会发生变化;
行链接无法避免,行迁移主要的原因就是pctfree这个参数太小,过大的话会导致空间浪费,要调整到合适
实践总结
35.优化器自动优化之查询转化
(1).排序消除:优化器对于sql中一些对查询结果没有作用和影响的排序会做消除,避免执行计划走排序,否则排序会导致无法走索引:eg:select count(
) from (select * from t_user order by id),这种执行计划不走排序
(2).去重消除,如果查找的字段中有主键或者唯一索引字段,则再出现distinct时会被消除,eg:select distinct fname from t_user 执行计划会走hash unique,但是如果字段中有fid(主键),则执行计划不再有hash unique,直接通过主键的唯一性查找;
(3)like转化:优化器会将不含"%“和”_"的like转化为等号
(4)常量的计算会提前完成
36.表访问路径
(1)全表扫描以多数据块读取的方式访问段中高水位线以下的数据块,多块读能够大大减少物理io,也仅仅全表扫描才多块读,全表扫描是否效率高取决于损益平衡点,当访问的数据高于这个点,则全表高效,否则索引访问高效;
这个点一般都是经验值比如10%
(2)常见全表扫描的场景
-1.大范围访问数据的情况,即访问的数据高于损益点
-2.表本身的数据量小,多块读取效率很高;
-3.使用full提示的情况下
(3)rowid扫描,只某一行所在的数据文件,数据块以及行在块中的位置,oracle中单行访问最快的一种方式,但局限性较大,因为rowid并不固定,会因为导入导出操作或者数据库版本而变化,直接通过where rowid=‘AAA…’ 则执行计划显示 user rowid;如果通过索引的则为 index rowid
(4).B索引扫描之原理b树索引的扫描过程先从数据字典里读到段头的块地址,该地址后面的就是树跟地址,然后定位到分支节点,然后到叶子节点,根据叶子节点位置决定从左到右或者从右向左的扫描,然后得到rowid从而获得所有满足条件的数据
-1索引扫描之索引唯一扫描,索引生效的要求:基于主键或者唯一性索引,且必须等值条件查询,sql语句经过解析后最终都 是以=等号的形式出现在谓词中的,即每次只能取出唯一一条记录,比如使用唯一索引where中用in(1,2,3)或者id=1 or id=2,最终解析后都是in变成or,or中循环使用唯一索引的
-2索引范围扫描,常见的出现场景:在唯一索引上使用范围操作符<=,>= ,<>,between等;非唯一索引都会走范围扫描复合索引上只用了部分字段,导致无法确定唯一一行
-3.降序索引范围扫描一般情况索引列值是升序排列的,如果order by 索引列(升序),则order 操作会被忽略,不会执行;如果是order by desc则会走index range scan descending
-4.索引全扫描发生的条件,查询的列都是索引列;索引列中至少有一个是not null,全表扫描会被替换成索引全扫描;全扫描扫描的过程指扫描全部叶子节点而非所有索引节点块,扫描时从根路径找到第一个叶子节点,叶子节点前后存在指针,实际上是双向链表,因为扫描本身是索引有序的,所以可以避免排序动作,或者order by 非空索引列,会走索引全扫描;对索引列求max和min都会走索引全扫描,只需要取最左和最优即可
-5索引快速扫描 参考 https://blog.csdn.net/leshami/article/details/7452310,
要求与索引全扫描一样,使用前提–1 所有出现的字段都为索引列,–2.基于上述前提count(*)操作几乎总是选择index fast full scan,而索引列上的order by子句几乎总是选择index full scan,–3.索引列至少有一个not null,—4.查出的行数占10%以上左右;索引快速扫描时多块io索引块,读取包括跟节点和分支节点在内的所有索引块,使用的时候选择性忽略一些,并行读取提高吞吐量,此外由于是多块读取的物理上顺序存储的块,并非像索引全扫描那样通过指针按逻辑顺序读取,所以读出的行是无序的
-6.索引关联,表间存在多个索引,索引连接通过ROWID,当然是否会走还是取决于成本的计算
-7.**count/count(stopKey)**是对rownum查询比如分页做的一种优化机制,具体原理不明
-8.INLIST ITERATOR 是在对索引列进行IN或者or操作时出现的重复多次查询的执行计划
37.表之间关联关系
(1).嵌套连接,连接过程:先从驱动表(外表)中读取一行,然后循环遍历整个被驱动表(内部表),找到匹配记录后返回到结果集,然后再读取外表下一行,再循环遍历整个内部表,相当于二层for循环,过程:


for(int i=0;i<length1;i++){                   //nested loops
    for(int j=0;j<length2;j++){             // nested loops
      getValue(j))  ;    //table access full 数据是循环一行行读的,不是一次性读完
            if(getvalue(i)==getvalue(j)){   //index unique scan
               saveIndexKey(j);
            }
        }
    }
    rtablevaluesbyROWID(indexkey);    //table access by index rowid

影响嵌套连接的两个因素
-1.外层循环次数循环次数越少,效率越高,所以驱动表一般优化器都选择返回结果集较少的那个表,使用提示 /+ordered use_nl(a,b)/ 可以让优化器按照顺序读取表,也就是说能够手动指定表的驱动顺序,不同的顺序带来的成本不同,在连接表比较多的情况下,加上该提示能够节省很多分析时间;三表或者多表连接时,应当选那些限制条件较多,或者交叉表作为驱动表(但也要看成本分析结果)
-2.内层查询效率,内层一般都能通过索引完成,要避免内层还使用全表扫描
(2).合并排序连接(merge join 和sort join)sort join 先对连接表的连接列做排序,然后merge join将排序之后的结果进行结果集合并,一般效率没有其他连接方式效率高,可用于>=、<等连接
(3)哈希连接 适合一个一个小表和大表的连接(指通过谓词(如果有的话)查询后返回的结果集的大小),通过对小表连接列的值做hash运算,然后对大表连接列做同样运算比较哈希值来匹配列;原理 https://www.cnblogs.com/mellowsmile/p/4839902.html ;只使用于等值连接
(4)其他连接方式:CARTESIAN 笛卡尔连接,一般很少用,因为会产生性能很差的sql;索引连接,当表中有多个索引列时,有可能会走这个连接,索引会通过rowid连接起来,本质还是哈希连接
(5)半连接(semi join), 指主查询和子查询之间的连接,句中含有子查询,子查询可以使用主查询上额列值,而反之不行,半连接处理方式包括:
-1.嵌套连接 当子查询的值提供给主查询做过滤条件时,子查询为驱动表,处理完子查询之后,条件中的字段都变成了常量;
-2Filter 查询 一种特殊的嵌套查询,原理 https://blog.csdn.net/colin_liu2009/article/details/7027206 ,当子查询需要用主查询的值来操作时,主查询就成了驱动表,filter与嵌套连接的区别是,会维护一个hash table,会将连接列的hash值放入这个table,当列值后续有重复,就不再做一次全表扫描,其实如果distinct 连接列的值个数比较少,则filter效率是比较高的,因为会少很多次内循环的全表扫描
IN 和 EXISTS效率问题?通过执行计划看是一样的
(6).反连接,当使用not in,not exists时会走ANTI JOIN反连接,一般都是hashjoin anti
排序:
38.引发排序的操作
-1.创建索引;2.order by,connect by,union去重等操作;3合并排序连接
39。优化排序的原则:能不走排序尽量不走,
-1 order by 后面的字段加索引,这样利用索引全扫描可以跳过排序的步骤
-2. 使用union all 代替unoin,去重操作会走排序
40.排序的三种方式最优排序,数据量不大的情况下,排序全发生在内存中,效率会高很多,10033事件能够看到排序并归段,如果没有,则说明是在内存中完成的排序,此时排序的效率很高;一遍排序和多遍排序都属于磁盘排序,效率比较差,当数据量比较大时,排序字段不可能全放在内存中,只能分段排序,排完一段放到临时文件上,最后再在内存中对每组最小值排序;
41.sort aggregate 执行步骤通常发生在聚合函数上,sum,count,avg等都会,并非真正的排序
42.sort unique排序去重,当下一步操作需要唯一值时会走这一步;
43.sot order by 当非索引排序时会出现,或者索引无法走索引全扫描时也会出现
44.buffer sort在内存中发生的排序动作,不表明一定发生了排序,但是一定是发生在内存中的,效率影响不大
45.子查询的两种处理方式:主查询优先,主查询执行结果作为外侧循环使用,内存子查询采取的策略是如果第一行被连接成功后立刻结束内侧循环,常见的及时FILTER循环;子查询优先,子查询的结果做唯一排序sort (或者hash
)unique,之后提供给主查询做条件
46.特殊称谓子查询:标量子查询 select a.fid,a.taskid,(select b.name_ from t_wf_task b where b.dbid_=a.ftaskid) from t_todo_todo a ,特点:查询结果单行单列,子查询效率很高,测试结果,标量子查询比内连接高效
注意:对于满足标量子查询的查询的
47. from中的子查询会称谓内联视图 执行计划VIEW
48. 子查询中特殊问题:
-1.空值问题,NOT IN中如果有空值存在,则查询结果始终为空
49.分区表分类,分区的优势,将大的物理段区分为小的物理段,避免全表扫描过多物理段,只访问指定分区
-1范围分区(使用最多),常用于日期时间、数字字段上
-2哈希分区
-3列表分区
-4间隔分区,基于范围分区的
-5.分区索引的使用 https://zhuanlan.zhihu.com/p/20809674
50.oracle中sql优化总结
https://docs.qq.com/sheet/BqI21X2yZIht1h0h6t1dnAOx0Yn67A2UwxKQ0jsJcO2Qw4EW1IQmKC2Cjyb922tRlj1Egjcr1GnvOf2yuWCF0?preview_token=&t=1564062554172&coord=B4A0A0&tab=BB08J2
51AWR简介 报告分析 https://blog.csdn.net/demonson/article/details/79474133
awr由来 http://blog.sina.com.cn/s/blog_6c25b1e50101gik0.html
52.优化实践:在单条sql中不要绑定大量的变量,绑定变量放在oracle内存的pga里的private sql area中,如果绑定太多,这块内存就会耗费很多,对内存冲击很厉害,比如以下的sql,该sql是在执行查询之前,公司分页框架先做了count,在参数比较多 比如5000个,那绑定变量就有1w个,对内存冲击太大,这种的一般用in ( ) or in ()拼接,比较过多绑定变量,正常情况下100-400合适,dba建议

select count(todo0_.FID) as col_0_0_ from T_todo_TODO todo0_ where (todo0_.FTASKID in (? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)) and (todo0_.FPRODUCTID in ('123123456' , '0' , '123123') or todo0_.FPRODUCTID is null)
Hibernate: select * from ( select todo0_.FID as FID373_, todo0_.FTASKTYPE as FTASKTYPE373_, todo0_.FASSIGNEE as FASSIGNEE373_, todo0_.FSUBJECT as FSUBJECT373_, todo0_.FSET_CODE as FSET5_373_, todo0_.FPRODUCTID as FPRODUCTID373_, todo0_.FAMOUNT as FAMOUNT373_, todo0_.FFRONTOPERATOR as FFRONTOP8_373_, todo0_.FACCEPTTIME as FACCEPTT9_373_, todo0_.FCREATETIME as FCREATE10_373_, todo0_.FTASKID as FTASKID373_, todo0_.FACCOUNT as FACCOUNT373_, todo0_.femergency as femergency373_, todo0_.fchainTypeName as fchainT14_373_, todo0_.fformurl as fformurl373_, todo0_.FexecutorFlag as Fexecut16_373_, todo0_.fywdate as fywdate373_, todo0_.FCONTACT as FCONTACT373_, todo0_.FPPSTRING as FPPSTRING373_ from T_todo_TODO todo0_ where (todo0_.FTASKID in (? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)) and (todo0_.FPRODUCTID in ('123123456' , '0' , '123123') or todo0_.FPRODUCTID is null) order by todo0_.femergency DESC, todo0_.FSET_CODE DESC ) where rownum <= ?

53.优化实践,查询字段过多确实很影响效率,如果联查表过多,无法优化的情况下,可以只查出自己需要的,对于不需要的字段不要查,如下:调用公司api查出sql1然后根据taskid循环处理,该sql要3s左右,优化业务逻辑为,同样的sql但只查询taskid这个字段就只花400ms左右,查出id再通过id去查需要的四个字段即可

---sql1
select taskimpl0_.DBID_               as DBID1_1144_0_,
       sofatask1_.DBID_               as DBID1_1150_1_,
       taskimpl0_.DBVERSION_          as DBVERSION3_1144_0_,
       taskimpl0_.NAME_               as NAME4_1144_0_,
       taskimpl0_.DESCR_              as DESCR5_1144_0_,
       taskimpl0_.STATE_              as STATE6_1144_0_,
       taskimpl0_.SUSPHISTSTATE_      as SUSPHIST7_1144_0_,
       taskimpl0_.ASSIGNEE_           as ASSIGNEE8_1144_0_,
       taskimpl0_.FORM_               as FORM9_1144_0_,
       taskimpl0_.PRIORITY_           as PRIORITY10_1144_0_,
       taskimpl0_.CREATE_             as CREATE11_1144_0_,
       taskimpl0_.DUEDATE_            as DUEDATE12_1144_0_,
       taskimpl0_.PROGRESS_           as PROGRESS13_1144_0_,
       taskimpl0_.SIGNALLING_         as SIGNALLING14_1144_0_,
       taskimpl0_.EXECUTION_ID_       as EXECUTION15_1144_0_,
       taskimpl0_.ACTIVITY_NAME_      as ACTIVITY16_1144_0_,
       taskimpl0_.HASVARS_            as HASVARS17_1144_0_,
       taskimpl0_.SUPERTASK_          as SUPERTASK18_1144_0_,
       taskimpl0_.EXECUTION_          as EXECUTION19_1144_0_,
       taskimpl0_.PROCINST_           as PROCINST20_1144_0_,
       taskimpl0_.SWIMLANE_           as SWIMLANE21_1144_0_,
       taskimpl0_.TASKDEFNAME_        as TASKDEF22_1144_0_,
       sofatask1_.taskId              as taskId1150_1_,
       sofatask1_.taskName            as taskName1150_1_,
       sofatask1_.userId              as userId1150_1_,
       sofatask1_.role                as role1150_1_,
       sofatask1_.executor            as executor1150_1_,
       sofatask1_.taskType            as taskType1150_1_,
       sofatask1_.executorFlag        as executor8_1150_1_,
       sofatask1_.usableFlag          as usableFlag1150_1_,
       sofatask1_.formUrl             as formUrl1150_1_,
       sofatask1_.componentCode       as compone11_1150_1_,
       sofatask1_.functionCode        as functio12_1150_1_,
       sofatask1_.operationCode       as operati13_1150_1_,
       sofatask1_.PROCESSINSTANCENAME as PROCESS14_1150_1_,
       sofatask1_.EXTDATA             as EXTDATA1150_1_
  from T_WF_TASK taskimpl0_
  left outer join T_WF_EX_TASK sofatask1_
    on taskimpl0_.DBID_ = sofatask1_.taskId, T_WF_EXECUTION executioni13_
 where taskimpl0_.PROCINST_ = executioni13_.DBID_
   and taskimpl0_.STATE_ = 'open'
   and sofatask1_.usableFlag = 1
   and (sofatask1_.executorFlag = 1 and
       sofatask1_.executor = '19072510195509001912' or
       sofatask1_.executorFlag = 0 and
       (sofatask1_.userId like '%#19072510195509001912#%' or
       (sofatask1_.functionCode || sofatask1_.operationCode in
       (select rolefuncti9_.FFUNC_CODE || rolefuncti9_.FOPERATION_CODE
             from T_ACL_ROLEFUNCTION rolefuncti9_
            where rolefuncti9_.FREL_ID in
发布了88 篇原创文章 · 获赞 5 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_42410730/article/details/96307653