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 序列中的分段号。