暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

避免同表的主键关联访问

651

第一章 适用范围

本案例中的问题现象发生于当前主流的ORACLE 11G环境。不排除随着后续版本的升级可能会有一定细微的差异表现。但问题的原因及解决方案,在所有版本中都是通用的。
数据库版本:ORACLE 11G

第二章 问题概述

问题SQL的主要现象是对同一个表访问了两次或多次,且关联列通过主键关联。产生的结果是对同一个表的多次访问。造成性能问题。

一般内层SQL在完成相应过滤后,如果外层有对其中部分表的主键关联访问,一般都是可以省去的,主键代表着非空唯一,因此内部的一条记录一定与外部相同表的记录一一对应。保证了数据既不会多也不会少。如果外部查询还有相应条件的话,也可以考虑放入内部做处理。

下面通过SQL案例进行说明:

SELECT *
    FROM T1_USER A
   WHERE USER_ID IN (SELECT A.USER_ID
                       FROM T1_USER A
                       LEFT JOIN T1_BIND B
                         ON LPAD(A.USER_ID, 32) = B.USER_ID
                      WHERE B.AGENT_CODE IS NULL
                        AND A.PLATFORM = '8')
     AND TO_CHAR(A.INSERT_TIME, 'yyyy-mm-dd') = :B1;

分析SQL语句结构,内部子查询中通过关联两个表获得内层结果,之后与外部查询再次通过USERID做关联后,通过INSERT_TIME做过滤返回最后结果。

两次访问同一张表,意味着消耗双倍的表资源访问。查看执行计划:

image.png

可以看到两次访问同一张表,且通过全表访问。造成SQL执行非常低效。

第三章 问题分析优化

出现上述执行低效的问题原因就是重复的表访问。
对内层而言,A表是对主键索引取前32个字符。经过分析测试及与业务沟通,该表LPAD(A.USER_ID, 32)与A.USER_ID列在数据上完全等价。只是由于字段设计为CHAR(64),为了去掉空格才这样写法。
image.png
B表的连接条件也是唯一键。
image.png
这样关联后,保证了内层查询不会出现重复记录。

对外层而言:
A表是通过主键索引与外层表做关联。主键索引的同表访问,是可以把外层条件放入内层,并去掉外部表的再次访问的。

调整后的SQL语句如下:

SELECT A.*
  FROM T1_USER A
  LEFT JOIN T1_BIND B
    ON LPAD(A.USER_ID, 32) =B.USER_ID
 WHERE B.AGENT_CODE IS NULL
   AND A.PLATFORM = '8'
   AND TO_CHAR(A.INSERT_TIME, 'yyyy-mm-dd') = '2022-08-15';

分析调整后的执行计划:
image.png

对于T1_USER表只访问了一次,且由于查询条件的增加,自动使用上了相应的索引。进一步降低了全表扫描的影响。性能提升有10倍以上。

分析是否还有其余优化空间:
可以看到,上面的T1_USER表预估行数与实际的差异较大。预估返回行数较多,导致了与T1_BIND的访问通过HASH连接。
出现问题的原因来自于条件列的函数访问:
TO_CHAR(A.INSERT_TIME, ‘yyyy-mm-dd’) = ‘2022-08-15’

去掉上述函数写法。改为如下测试:

SELECT A.*
  FROM T1_USER A
  LEFT JOIN T1_BIND B
    ON LPAD(A.USER_ID, 32) =B.USER_ID
 WHERE B.AGENT_CODE IS NULL
   AND A.PLATFORM = '8'
   and A.INSERT_TIME>=date'2022-08-15' and A.INSERT_TIME<date'2022-08-15'+1;

查看调整后的执行计划:
image.png

调整后,预估行数变小了,对于被驱动表,也从HASH关联调整为NL连接,且有合适的索引给与支撑。提升效率明显。
另外查看T1_USER表,索引访问也去掉了filter部分改为使用access。进一步得到了性能提升。

第四章 解决方案

经过上述章节的分析,主要通过以下手段优化:

  1. 对于主键关联的表,降低重复访问,并将条件放入最内层;
  2. 去除索引列上的函数动作,提升查询效率。

第五章 问题总结

本案例中,最重要的优化是去掉了重复的表访问,此时将所有的过滤条件都放入了最内层执行。数据库是会考虑所有的路径走出相对优异的执行计划的(省去一次表访问并改为索引访问)。
正是这一步的改进,再结合后续的去掉索引列的函数转换动作。进一步提升了SQL的执行效率。

最后修改时间:2022-08-22 12:26:32
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论