WHERE 中的 IN 是要过滤出某字段值包含在给定枚举值集合中的记录,比如:查出某几个城市的客户、某些类型的订单等等。数据库做 IN 过滤时,要用字段值和值集合的成员作比较计算。
一、WHERE中有很多IN判断怎么提速
WHERE 中的 IN 是要过滤出某字段值包含在给定枚举值集合中的记录,比如:查出某几个城市的客户、某些类型的订单等等。
数据库做 IN 过滤时,要用字段值和值集合的成员作比较计算。若采用顺序查找,要比较 1 到 n 次(n 是值集合大小)。即使在值集合有序的情况下用二分法查找,也要比较数次。数据量较大时比较次数会非常多,IN 的速度就会很慢,而且值集合越大速度越慢。
如果在过滤时不再做比较计算,性能就能得到大幅提高!
首先,确定 IN 可能取值的列表。可能值通常不会太多,一般都保存在一个选项表中。如果没有现成的选项表,要遍历原数据得到所有可能值,保存成一个选项表。然后转换原数据,把 IN 字段值替换为选项表中对应记录的序号(位置),另存成一份新数据。
对替换后的新数据做 IN 判断时,先生成一个与选项表等长的布尔值集合,其第 i 个值由选项表的第 i 个成员是否在 IN 的值集合中决定,在就是 true,不在就是 false。
然后遍历新数据,用 IN 字段值(也就是选项表的序号)去取布尔值集合中的成员,是 true 就符合过滤条件,否则就不符合。
这种方法本质上是将“集合值比较”转换为“序号引用”,省去了比较计算,性能会大幅提升。而且计算时间和值集合大小无关,不会随着 IN 枚举值的增多而增加。
但是,SQL 不支持通过序号(位置)直接取集合中的成员,无法实现这种优化方法。
集算器 SPL 支持序号引用,可以很方便的实现这个优化方法。
1、 数据预处理,转换为序号。
=cs.run(dim1.pos@b(f1):f1),遍历原数据,使用 pos 函数查到原数据 f1 字段在选项表 dim1 中对应记录的序号,用这个序号代替原来的 f1 字段值,另存一份新数据。dim1 预先按照 f1 对应值排好序了,所以这里采用二分法查找,预处理的速度更快。
2、 对预处理好的新数据做 IN 过滤计算。
假设传入值集合为 arg_F1,生成布尔值集合的代码是:
b1=dim1.(arg_F1.contain@b(~)),arg_F1 有序,所以这里也是二分法查找。
然后就可以用新方法过滤新数据了:
=file(“T.ctx”).open().cursor(…;b1(f1) && …),用 f1 中的序号,直接去取布尔值集合中的成员,成员是 true 则满足过滤条件,否则就不满足。这时不需要再做比较计算,性能会大幅提高。
实测表明,在同等硬件环境下,用 SPL 实现的这种方案比 Spark 上的 SQL 快了上百倍。
延伸阅读:
二、联合索引 (a,b,c)
联合索引 (a,b,c) 实际建立了 (a)、(a,b)、(a,b,c) 三个索引。
我们可以将组合索引想成书的一级目录、二级目录、三级目录,如index(a,b,c),相当于a是一级目录,b是一级目录下的二级目录,c是二级目录下的三级目录。要使用某一目录,必须先使用其上级目录,一级目录除外。
联合索引的优势
1) 减少开销
建一个联合索引 (a,b,c) ,实际相当于建了 (a)、(a,b)、(a,b,c) 三个索引。每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销!
2)覆盖索引
对联合索引 (a,b,c),如果有如下 sql 的,
SELECT a,b,c from table where a=’xx’ and b = ‘xx’;
那么 MySQL 可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机 io 操作。减少 io 操作,特别是随机 io 其实是 DBA 主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。
3)效率高
索引列多,通过联合索引筛选出的数据越少。比如有 1000W 条数据的表,有如下SQL:
select col1,col2,col3 from table where col1=1 and col2=2 and col3=3;
复制
假设:假设每个条件可以筛选出 10% 的数据。
- A. 如果只有单列索引,那么通过该索引能筛选出 1000W10%=100w 条数据,然后再回表从 100w 条数据中找到符合 col2=2 and col3= 3 的数据,然后再排序,再分页,以此类推(递归);
- B. 如果是(col1,col2,col3)联合索引,通过三列索引筛选出 1000w10% 10% *10%=1w,效率提升可想而知。