用 TPCH 练习性能优化 Q17

一、 SQL及分析

查询SQL语句如下:

select
    sum(l_extendedprice) / 7.0 as avg_yearly
from
    lineitem,part
where
    p_partkey = l_partkey
    and p_brand = 'Brand#33'
    and p_container = 'LG DRUM'
    and l_quantity < (
        select
            0.2 * avg(l_quantity) 
        from
            lineitem
        where
            l_partkey = p_partkey
    );

这种在子查询中用等值条件与主表关联的情况,都可以转换成JOIN来计算,相当于如下SQL

select
    sum(l_extendedprice) / 7.0 as avg_yearly
from
    lineitem,part,
    (select 
        l_partkey lp, 
        0.2*avg(l_quantity) lq
    from 
        lineitem
    group by 
        l_partkey
    ) lpq
where
    p_partkey = l_partkey
    and p_brand = 'Brand#33'
    and p_container = 'LG DRUM'
    and l_partkey = lp
    and l_quantity < lq;

子查询是分组聚合运算,主查询就是多表关联后的聚合运算。

二、 SPL实现


A

1

=now()

2

>brand="Brand#33"

3

>container="LG DRUM"

4

=file("part.ctx").open().cursor@m(P_PARTKEY;P_BRAND==brand && P_CONTAINER==container).fetch().keys@im(P_PARTKEY)

5

=file("lineitem.ctx").open()

6

=A5.cursor@m(L_PARTKEY,L_QUANTITY;A4.find(L_PARTKEY))

7

=A6.groups@u(L_PARTKEY;avg(L_QUANTITY):avg).run(avg/=5).keys@im(L_PARTKEY)

8

=A5.cursor@m(L_PARTKEY,L_QUANTITY,L_EXTENDEDPRICE;L_PARTKEY:A7)

9

=A8.total(sum(if(L_QUANTITY<L_PARTKEY.avg,L_EXTENDEDPRICE,0)))/7.0

10

=interval@ms(A1,now())

三、 进一步优化

1. 优化方法

本例中要使用Q1中介绍的字符串整数化方法,part中的P_BRAND已在Q16中转化,本例还需转化P_CONTAINER。还要使用Q2中介绍的维表主键序号化方法,part中的P_PARTKEY已在Q16中转化,lineitem中的L_PARTKEY已在之前的例子中转换过了

2. 数据转换代码

2.1 lineitem转换

复制lineitem_15.ctx重命名为lineitem_17.ctx

2.2 part转换


A

1

=file("part_16.ctx").open().cursor().fetch()

2

=A1.id(P_CONTAINER).sort()

3

=file("p_container.txt").export(A2)

4

=A1.run(P_CONTAINER=A2.pos@b(P_ CONTAINER))

5

=file("part_17.ctx").create(#P_PARTKEY, P_NAME,P_MFGR, P_BRAND, P_TYPE, P_SIZE, P_CONTAINER, P_RETAILPRICE, P_COMMENT)

6

>A5.append(A4.cursor())

3. 数据转换后的计算代码

维表和字符串字段取值列表需要预加载,加载代码如下:


A

1

>env(p_brand,file("p_brand.txt").import@si())

2

>env(p_container,file("p_container.txt").import@si())

3

>env(part, file("part_17.ctx").open().import())

在查询计算之前,需要先运行预加载代码,将小维表读入内存。

计算代码:


A

1

=now()

2

>brand=p_brand.pos@b("Brand#33")

3

>container=p_container.pos@b("LG DRUM")

4

=part.@m(P_BRAND==brand && P_CONTAINER==container)

5

=file("lineitem_17.ctx").open()

6

=A5.cursor@m(L_PARTKEY,L_QUANTITY;A4(L_PARTKEY))

7

=A6.groups@u(L_PARTKEY;avg(L_QUANTITY):avg).run(A4(L_PARTKEY)=avg*0.2)

8

=A5.cursor@m(L_PARTKEY,L_QUANTITY,L_EXTENDEDPRICE;L_PARTKEY:A4:#)

9

=A8.total(sum(if(L_QUANTITY<L_PARTKEY,L_EXTENDEDPRICE,0)))/7.0

10

=interval@ms(A1,now())

四、 使用企业版列式计算

1. 原始数据


A

1

=now()

2

>brand="Brand#33"

3

>container="LG DRUM"

4

=file("part.ctx").open().cursor@mv(P_PARTKEY;P_BRAND==brand && P_CONTAINER==container).fetch().keys@im(P_PARTKEY)

5

=file("lineitem.ctx").open()

6

=A5.cursor@mv(L_PARTKEY,L_QUANTITY;A4.find(L_PARTKEY))

7

=A6.groups@u(L_PARTKEY;avg(L_QUANTITY)*0.2:avg).keys@im(L_PARTKEY)

8

=A5.cursor@mv(L_PARTKEY,L_QUANTITY,L_EXTENDEDPRICE;L_PARTKEY:A7)

9

=A8.total(sum(if(L_QUANTITY<L_PARTKEY.avg,L_EXTENDEDPRICE,0)))/7.0

10

=interval@ms(A1,now())

2. 优化后数据

维表和字符串字段取值列表需要预加载,加载代码如下:


A

1

>env(p_brand,file("p_brand.txt").import@si())

2

>env(p_container,file("p_container.txt").import@si())

3

>env(part, file("part_17.ctx").open().import@v())

在查询计算之前,需要先运行预加载代码,将小维表读入内存。

计算代码:


A

1

=now()

2

>brand=p_brand.pos@b("Brand#33")

3

>container=p_container.pos@b("LG DRUM")

4

=part.@m(P_BRAND==brand && P_CONTAINER==container)

5

=file("lineitem_17.ctx").open()

6

=A5.cursor@mv(L_PARTKEY,L_QUANTITY;A4(L_PARTKEY))

7

=A6.groups@u(L_PARTKEY;avg(L_QUANTITY):avg)

8

clear A4

9

=part.(null)

10

=A7.run@m(A9(L_PARTKEY)=avg*0.2)

11

=A5.cursor@mv(L_PARTKEY,L_QUANTITY,L_EXTENDEDPRICE;L_PARTKEY:A9:#)

12

=A11.total(sum(if(L_QUANTITY<L_PARTKEY,L_EXTENDEDPRICE,0)))/7.0

13

=interval@ms(A1,now())

五、 测试结果

单位:秒


常规

列式

优化前

15.4

7.7

优化后

14.8

6.8

本例lineitem过滤后数据记录只有60多万条,数据量太少,序号化的效果体现不出来,优化效果不明显。如果改变A4中的过滤条件,将&&改成||lineitem过滤后有3800多万条数据,此时再测试,优化效果就非常明显。