SQL 提速:按段分组统计
【摘要】
从原理上分析 SQL 语句慢的原因,用代码示例给出提速办法。点击了解SQL 提速:按段分组统计
问题描述
数据表 T 的字段 x 按照集合 X={X1<X2<…<Xn}做分段汇总,即第 0 段是 x<X1,第 i 段(0<i<n)是左闭右开区间 [Xi<x<Xi+1),第 n 段是 x>=Xn。分段之后,按照段号分组汇总。有时分段也可能是左开右闭区间。
分段汇总 SQL 一般用 case when 写出来:
select xgroup,sum(x),avg(x) from
(select case
when x< X1 then 0
when x< X2 then 1
…
else n
end as xgroup,
x
from T)
group by xgroup
case when 语句会依次判断条件,如果满足则结束;如果不满足则判断下一个条件,每条记录的 x 字段都要经过多次比较来确定分段。最少要比较 1 次,最多要 n-1 次,平均比对次数是 n/2,复杂度是 O(n)。在分段个数比较多,总数据量比较大的时候,性能较差。
而且,当 X1,X2,…,Xn 动态变化的时候(比如前端传入的参数),SQL 就很难实现了,更谈不上性能优化。
提速方案
因为 X 集合有序,我们可以利用二分法来提速,复杂度为 O(log2n)。在 n 较大时就会比顺序分段有非常明显的优势。在 n 较小时,因为二分法的过程相对复杂,反而可能比顺序分段更慢。通常 n>=8 时,二分法就会有优势。X 成员一般是几个到几十个,所以即使 X 集合无序,也可以先在内存中排序,对整体性能影响可以忽略。
这种方法,可以很容易的将 X 集合作为参数传入,动态计算分段。
代码示例
先定义参数 arg_X,传入 X 的分段序列。例如,arg_X 参数值为 [0,100,230,450,480,566,798…]。arg_X 无序时要先排序。
A |
|
1 |
=arg_X.sort() |
2 |
=file("T.ctx").open().cursor(x) |
3 |
=A2.groups(A1.pseg(x):xgroup;sum(x),avg(x)) |
A1:传入参数升序排序。
A2:打开组表,建立游标,只取出 x 字段。
A3:分段不会太多,所以采用小结果集分组,对 x 求和、求平均。分组表达式用 pseg 函数,采用二分法计算字段 x 在 A1 序列中的分段号。