书接上回
根据实际的业务需求
查询由
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022;
几经转换后,变成了如下的查询:
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022 and city_code||‘.’||int_code in (‘122.9’,‘13.11’,‘6.3’); (实际sql中in的条件为27个,我就不在这里赘述了)
这样的查询在实际的执行过程中,效果很差。
oracle并没有走我们在city_code与int_code上创建的索引。执行计划上,两个code拼接,是or。
当然这也并不难理解,虽然鲜少有人会在索引失效的原因里把拼接字符列在其中,但显然这种操作在我的理解下也是函数的一种。
现在这样,当然和我们实际的诉求相差较远。
我这个时候建议开发的同事把这一条拆开成若干条。
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022 and city_code=:A and int_code = :B;
后两个条件在代码中使用绑定变量的方式,然后多线程一起跑。
开发的同事半天不语,沉默良久后表示,不现实。原因在于,这两个code在代码中是要根据前一个流程的计算得出的,每一次跑出来的结果数量并不总是相同的,并且在量级上相差较多,很难每次部署的时候都要去计算分配资源。
我询问开发的同事,两个code是在数据库中的查询得到的吗?
如果是,可以试着做join,在关联的column上建索引,也许会比现在少。
很可惜,并不是。
当时的气氛有些压抑,因为感觉月报上可以写,把查询时间降低了95%这样的傲人战绩了。结果现在只降了70%,和老板的诉求还是有差距的。
join?
你说,select 122 as city_code, 9 as int_code from dual; 算不算一种join?
我说,dual的确是一张表,但是没见人这么写过。
开发同事推了下眼睛,那我们今天就给他们开开眼!
于是,这条查询变成了:
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022 and (city_code,int_code) in
(select 122 as city_code, 9 as int_code from dual union all
select 13 as city_code, 11 as int_code from dual union all
select 6 as city_code, 3 as int_code from dual
);
(实际sql中 union all的条件为27个,我就不在这里赘述了)
虽然很不美观,但是神奇的事情发生了,cost由300多降到了40.
索引,数据库承认了,nested loops也启动了,hash join也在,回来了,都回来了。
就这样一条简洁费时的sql,历经一周半,变成了一条冗长但高效的sql。执行计划的cost由最开始的接近两万,到现在的40。
虽然,这是一条28张“表”的union all,或许很多人看到他第一眼的瞬间就放弃了这个不美观,不合理的想法。但实际情况却超出了我们所有人的预期。
任何大胆的想法都去试试吧,世界会给你意想不到的回馈。