业务SQL中包含OR, 如果发现性能对比没有这个OR下降极大,可以考虑本文介绍的OR优化试试。包括在过滤条件里的OR以及JOIN条件里的OR两种情况。
所有改写都是在默认执行计划的优化效果不好时,看看这个手工指定的优化是否更好一些。性能是否提高以及最终结果是否正确,都建议多加验证。
目录导航
过滤条件
分为两种情况,同一个列不同的值,以及不同的列不同的值(前者算后者的特殊情况,但更常见。核心就是手工将多个OR判断,改成1个判断。
A=XX OR A=YY 以及 A in (XX,YY)的写法
改写成适合数字的写法
decode(A,XX,XX,YY,XX,'')=XX
也就是把YY值改成XX,变成都判断等值XX
改成case的写法,适合字符
case A when XX then XX when YY then XX end)=XX
gbase> select * from t3 where id=1 or id=11;
+------+------+
| id | id2 |
+------+------+
| 1 | 1 |
| 11 | 11 |
+------+------+
2 rows in set (Elapsed: 00:00:00.07)
gbase> select * from t3 where decode(id,1,1,11,1)=1;
+------+------+
| id | id2 |
+------+------+
| 1 | 1 |
| 11 | 11 |
+------+------+
2 rows in set (Elapsed: 00:00:00.08)
A=XX OR B=YY or C=ZZ
改写成
concat(case when a=XX then XX else '' end,case when B=YY then XX else '' end,case when C=ZZ then XX else '' end) like 'XX%'
其中的XX,YY可以是相同值或者不同值。 核心也是将多个OR判断,改成1个判断。
gbase> select * from t3 where id=111 or id2=9999;
+------+------+
| id | id2 |
+------+------+
| 111 | 1111 |
+------+------+
1 row in set (Elapsed: 00:00:00.04)
gbase> select * from t3 where concat(case when id=111 then 111 else '' end,case when id2=9999 then 111 else '' end) like '111%';
+------+------+
| id | id2 |
+------+------+
| 111 | 1111 |
+------+------+
1 row in set (Elapsed: 00:00:00.09)
JOIN条件 要求结果集没有膨胀
如下的JOIN里,OR条件
select b.XX from a
left join b on (a.id=b.id or a.name=b.name) and ...其它join条件
改写成2个join
select coalesce(b.XX,b2.XX) from a
left join b on a.id=b.id and …其它join条件
left join b b2 on a.id<>b2.id and a.name=b2.name and …其它join条件
要求结果集没有膨胀,比如a表1万行,结果集依然是左表的1万行。也就是1对1的场景,右表不能出现重复值,导致左表膨胀。
否则因左表出现重复,1:n,导致改写后的方案结果集行数少了。
而select部分用到b表的列,需要根据b和b2的列进行选择,比如b.XX 变成 coalesce(b.XX,b2.XX)
gbase> select * from t1;
+------+------+
| id | id2 |
+------+------+
| 1 | 66 |
| 2 | 77 |
+------+------+
2 rows in set (Elapsed: 00:00:00.06)
gbase> select * from t3;
+------+------+
| id | id2 |
+------+------+
| 1 | 66 |
| 3 | 77 |
| 9 | 99 |
+------+------+
3 rows in set (Elapsed: 00:00:00.09)
gbase> select * from t1 left join t3 on t1.id=t3.id or t1.id2=t3.id2;
+------+------+------+------+
| id | id2 | id | id2 |
+------+------+------+------+
| 1 | 66 | 1 | 66 |
| 2 | 77 | 3 | 77 |
+------+------+------+------+
2 rows in set (Elapsed: 00:00:00.28)
gbase> select * from t1 left join t3 on t1.id=t3.id left join t3 t3_2 on t1.id<>t3_2.id and t1.id2=t3_2.id2;
+------+------+------+------+------+------+
| id | id2 | id | id2 | id | id2 |
+------+------+------+------+------+------+
| 2 | 77 | NULL | NULL | 3 | 77 |
| 1 | 66 | 1 | 66 | NULL | NULL |
+------+------+------+------+------+------+
2 rows in set (Elapsed: 00:00:00.28)
gbase> select t1.id,t1.id2,coalesce(t3.id,t3_2.id)id,coalesce(t3.id2,t3_2.id2)id2 from t1 left join t3 on t1.id=t3.id left join t3 t3_2 on t1.id<>t3_2.id and t1.id2=t3_2.id2;
+------+------+------+------+
| id | id2 | id | id2 |
+------+------+------+------+
| 2 | 77 | 3 | 77 |
| 1 | 66 | 1 | 66 |
+------+------+------+------+
2 rows in set (Elapsed: 00:00:00.16)
总结
本文的OR优化,都趋向于手工调整执行计划,特别是过滤条件。 而JOIN的OR优化,必须保证1:1,不能是1:n。否则结果集可能是错的。