最近在项目中有个慢sql执行查询需要100多秒,查询语句的结构其实很简单,但是调整不到想要的计划,最终发现是数据类型影响的,调整后获得理想计划。
SELECT
b.YC_ID ,
b.YC_NAME ,
b.xxx
...
b.DEVICE_ID ,
'2021-12-27' as DEVICETIME ,
ROUND(TO_NUMBER(data_0_0), 6) as CUR_VALUE
FROM
TEST1_FQ202112 a
inner join TEST2 b
on
a.data_id = b.yc_id
WHERE
A.OCCUR_DATE='2021-12-27';
SQL语句如上所示,其中a表为分区表的一个子分区(分区列occur_date,当前分区子表500万数据量),b表为普通表(1万条数据量),其中A.OCCUR_DATE='2021-12-27’过滤后有18万,但是occur_date 加yc_id能够达到万分之一的过滤性。
经过上述简单分析可以看出,a和b表应该做nest loop index join,且a表走(occur_date,data_id)复合索引最合适,那我们看一下实际计划:
执行时间在:
实际计划为两表做了hash join,这完全不符合预期,查看a表cscn2的预估结果集为1395257,显然a表的统计信息不准确,那我们更新下统计信息并且禁用hash join,在mpp环境下强制做nl index试试:
通过上述计划可以看到,计划的确朝着我们的引导方向走了,连接方式为nest loop index join,a表为索引定位,但是这是需要注意到的是a表索引定位方式为scan_range[(exp_cast(‘2021-12-27’),min),(exp_cast(‘2021-12-27’),max)],这貌似不对啊!虽然是ssek2,但实际上该索引只是利用到了前导列occur_date,而data_id没有用上,估计效率不会高,因为occur_date过滤完依然也有近20万的数据,加上回表和连接效率估计不会高到哪里去,二话不说实际执行以下看下效果吧!
那为什么data_id无法走索引定位呢?其实我们仔细观察执行计划就能看出端倪:
a.data_id和b.yc_id被放到了两表连接之后,并且有exp_cast操作,这时候就该想到这两个数据类型是否不一致啊。确认后果然a.data_id为bigint,而b表yc_id为numeric(20,0),存在数据类型内部转换。
那我们将sql语句改动一下,将连接列做一下数据类型转换:
a.data_id = cast(b.yc_id as bigint)
这时候我们发现执行计划变成了我们理想的计划了:
执行耗时为0.049ms,大功告成!
在进行sql语句优化过程中,有时候数据类型不一致并不会导致结果集或执行通过率,但是有时候会导致偏离理想的执行计划,大幅降低执行效率,当执行计划出现了exp_cast则一定要引起足够的重视!
文章
阅读量
获赞