oracle中谓词带OR语句优化

2022-08-23 19:21:37 浏览数 (1)

【背景】

根据研发提供的慢SQL,分析Oracle AWR中SQL,并没有发现相同的SQL.发现类似SQL,只是谓词条件不一样,咨询研发得知,前端根据登录人的角色不同,SQL写法也会变化,通常优化28原则,虽然这个功能用的少,但影响用户体验。

经常会听说,怎么前端传值不一样或者不同用户访问,性能差别很大。本次这个SQL,也是类似情况。逻辑如下,总部人员登录直接赋值总部代码即可,就当前登录人若是分部,需要查找分部下面的人,若分部下面还有分部,也把下面的分部对应的人,查找到(最多2层关系)。(备注Oracle 11.2.0.4)

【具体SQL】

备注:生产上用的最多是mvOrg.CODE等于那个值,加个or后SQL比不加or慢几十倍且性能差.

代码语言:javascript复制
SELECT *
  FROM (SELECT TMP.*, ROWNUM ROW_ID
FROM (SELECT DISTINCT E.ID,
E.CODE,
 E.NAME,
E.mobile,
mvOrg.CODE   AS orgCode,
mvOrg.NAME   AS orgName
 FROM XIAOXU.T_TEST_EMPLOYEE E
JOIN XIAOXU.T_TEST_USER U
ON U.USER_ID = E.ID                  
JOIN XIAOXU.T_TEST_USER_ORG G
ON G.USER_ID = U.USER_ID
JOIN XIAOXU.MV_TEST_ORG_ORDER mvOrg
ON mvOrg.ID = G.ORG_ID
 where 1 = 1
and (mvOrg.CODE in
(SELECT code
from XIAOXU.T_TEST_ORG
 where PARENT_ID in
  (SELECT id
from XIAOXU.T_TEST_ORG
  where code = '120168')) or
mvOrg.CODE = '120168')
and G.STATUS = 'VALID'
 and E.in_service = 'ACTIVE') TMP
WHERE ROWNUM <= 10)
 WHERE ROW_ID > 0
 执行时间如下:
 Elapsed: 00:00:05.53

【执行计划】

存在问题:

1、返回10条记录消耗140万buffer gets

2、先进行hash join,得到37万数据,进行filter,然后判断是否满足filter(("MVORG"."CODE"='120168' OR IS NOT NULL)),filter性能特别差,通常来说filter效率不高(NL特例)--需要进行优化,消除FILTER。

3、filter访问被驱动表,被驱动表执行27次(13->NESTED LOOPS)

【分析SQL】

1、分页返回TOP 10记录,这个分页框架写法是正确,其实2层就够,这个写法没有啥问题,主要为了兼容下一个页语法。

2、执行计划产生FILTER,导致执行效率低,通常来说FILTER执行效率低。一般可以通过hint或者改写来消除FILTER从而提升效率。12C中对简单OR会进行改写。但低版本中通常需要改写或强制hint。那么如何消除FILER,通过改写SQL或者HINT方式,本次通过改写SQL。

【改写逻辑】

1、通过OR改写是通过union all LNNVL函数去重.

2、本次案例中是mvOrg.CODE in ( xx OR xx),oracle中in和exists半连接本身就自动去重功能,所以第一种改写使用union all方式.

【改写SQL】

代码语言:javascript复制
(mvOrg.CODE in
(SELECT code
from XIAOXU.T_TEST_ORG
 where PARENT_ID in
 (SELECT id
 from XIAOXU.T_TEST_ORG
  where code = '120168')) or
mvOrg.CODE = '120168')
改写成如下格式:
(mvOrg.CODE in
(SELECT code
from XIAOXU.T_TEST_ORG
where PARENT_ID in
(SELECT id
from XIAOXU.T_TEST_ORG
 where code = '120168') union all
select  '120168' from dual))
代码语言:javascript复制
 SELECT *
  FROM (SELECT TMP.*, ROWNUM ROW_ID
FROM (SELECT DISTINCT E.ID ,
E.CODE,
E.NAME ,
E.mobile ,
E.in_service ,
mvOrg.CODE,
  mvOrg.NAME
FROM XIAOXU.T_TEST_EMPLOYEE E
JOIN XIAOXU.T_TEST_USER U
 ON U.USER_ID = E.ID
JOIN XIAOXU.T_TEST_USER_ORG G
 ON G.USER_ID = U.USER_ID
JOIN XIAOXU.MV_TEST_ORG_ORDER mvOrg
ON mvOrg.ID = G.ORG_ID
  where 1 = 1
and (mvOrg.CODE in
(SELECT code
from XIAOXU.T_TEST_ORG
 where PARENT_ID in
(SELECT id
from XIAOXU.T_TEST_ORG
where code = '120168') union all
 select  '120168' from dual))
 and G.STATUS = 'VALID'
 and E.in_service = 'ACTIVE') TMP
WHERE ROWNUM <= 10)
 WHERE ROW_ID > 0
 【执行时间】
 Elapsed: 00:00:00.04

【改写后执行计划】

1、执行计划由filter变成NL方式。

2、buffer gets从140万降低到442返回10条记录.不管从时间还是资源消耗来讲,提升N个数据量级别。

3、执行时间从5s变成0.04s

【特殊改写方式】

1、由于部门是树形结构,可以通过connect by来实现,经过了解本次分部这个层级最多是2层。如果存在更多层,那么与实际逻辑不一样。经过测试效率与上面union all保持一致。

代码语言:javascript复制
  mvOrg.CODE in
(SELECT code
  from XIAOXU.T_TEST_ORG
start with  code = '120168'
connect by prior ID=PARENT_ID
 )

【总结】

1、or通常使用union all LNNVL来消除filter执行计划,本次案例是in里面语句,所以无需去重,因为in与exists具备消除重复功能。

2、本次or前面具备树形层级关系,所以可以通过connect by来改写,必须树形结构层级,因为递归检索的,只适合特定场景,否则改写就不等价。

0 人点赞