用 TPCH 练习性能优化 Q18

一、 SQL及分析

查询SQL语句如下:

select * from (
    select
        c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice,
        sum(l_quantity)
    from
        customer, orders, lineitem
    where
        o_orderkey in (
            select
                l_orderkey
            from
                lineitem
            group by
                l_orderkey
            having
                sum(l_quantity) > 314
        )
        and c_custkey = o_custkey
        and o_orderkey = l_orderkey
    group by
        c_name,
        c_custkey,
        o_orderkey,
        o_orderdate,
        o_totalprice
    order by
        o_totalprice desc,
        o_orderdate
) where rownum<=100;

分析这个查询,如果将下面的子查询

select 
    l_orderkey, sum(l_quantity) lq
from 
    lineitem
group by 
    l_orderkey

命名为视图lo,则原查询的主体等价于:

select * from (
    select
        c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, lq
    from
        customer, orders, lo
    where
        c_custkey = o_custkey
        and o_orderkey = l_orderkey
        and lq > 314
    order by
        o_totalprice desc,
        o_orderdate
) where rownum<=100;

视图是个单表分组聚合运算,主查询是多表关联过滤。

二、 SPL实现

这是一个有外键关联的表orders与其同维表lo的连接运算,而我们知道lineitemorders的子表,也是按l_orderkey排序的,用lineitem计算出来的lo仍然可以保证按l_orderkey有序,这样可以与orders做高速归并连接。

customer作为外键表在结果集上与orders关联即可,前面运算可以不参与。


A

1

=now()

2

>quantity=314

3

=file("lineitem.ctx").open().cursor@m(L_ORDERKEY,L_QUANTITY)

4

=A3.group@s(L_ORDERKEY;sum(L_QUANTITY):quantities).select(quantities>quantity)

5

=file("orders.ctx").open().new(A4,O_ORDERKEY,O_CUSTKEY,O_TOTALPRICE,O_ORDERDATE,quantities)

6

=A5.total(top(100;-O_TOTALPRICE,O_ORDERDATE))

7

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

8

=A6.joinx@q(O_CUSTKEY,A7:C_CUSTKEY,C_NAME)

9

return interval@ms(A1,now())

A5中用前面例子介绍过的主子表有序归并关联技巧,A8中用joinx@qcustomer表有序连接。

三、 进一步优化

1. 优化方法

本例中要使用Q2中介绍的维表主键序号化方法,customer中的C_CUSTKEYorders中的O_CUSTKEY都已在之前的例子中转换过了

2. 数据转换代码

复制customer_13.ctxorders_13.ctxlineitem_17.ctx分别重命名为customer_18.ctxorders_18.ctxlineitem_18.ctx

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

维表需要预加载,加载代码如下:


A

1

>env(customer, file("customer_18.ctx").open().import())

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

计算代码:


A

1

=now()

2

>quantity=314

3

=file("lineitem_18.ctx").open().cursor@m(L_ORDERKEY,L_QUANTITY)

4

=A3.group@s(L_ORDERKEY;sum(L_QUANTITY):quantities).select(quantities>quantity)

5

=file("orders_18.ctx").open().new(A4,O_ORDERKEY,O_CUSTKEY,O_TOTALPRICE,O_ORDERDATE,quantities)

6

=A5.total(top(100;-O_TOTALPRICE,O_ORDERDATE))

7

=A6.derive(customer(O_CUSTKEY).C_NAME:C_NAME).run(O_ORDERDATE=date@o(O_ORDERDATE))

8

=interval@ms(A1,now())

orders_18.ctx中的O_ORDERDATE在以前的例子中被整数化了,所以需要用date@o再还原成日期。

四、 使用企业版列式计算

1. 原始数据


A

1

=now()

2

>quantity=314

3

=file("lineitem.ctx").open().cursor@mv(L_ORDERKEY,L_QUANTITY)

4

=A3.group@s(L_ORDERKEY;sum(L_QUANTITY):quantities).select@v(quantities>quantity)

5

=file("orders.ctx").open().new(A4,O_ORDERKEY,O_CUSTKEY,O_TOTALPRICE,O_ORDERDATE,quantities)

6

=A5.total(top(100;-O_TOTALPRICE,O_ORDERDATE))

7

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

8

=A6.joinx@q(O_CUSTKEY,A7:C_CUSTKEY,C_NAME)

9

=interval@ms(A1,now())

2. 优化后数据

维表需要预加载,加载代码如下:


A

1

>env(customer, file("customer_18.ctx").open().import@v())

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

计算代码:


A

1

=now()

2

>quantity=314

3

=file("lineitem_18.ctx").open().cursor@mv(L_ORDERKEY,L_QUANTITY)

4

=A3.group@s(L_ORDERKEY;sum(L_QUANTITY):quantities).select@v(quantities>quantity)

5

=file("orders_18.ctx").open().new(A4,O_ORDERKEY,O_CUSTKEY,O_TOTALPRICE,O_ORDERDATE,quantities)

6

=A5.total(top(100;-O_TOTALPRICE,O_ORDERDATE))

7

=A6.derive(customer(O_CUSTKEY).C_NAME:C_NAME).run(O_ORDERDATE=date@o(O_ORDERDATE))

8

=interval@ms(A1,now())

五、 测试结果

单位:秒


常规

列式

优化前

13.7

5.0

优化后

13.7

4.9

数据转换并预加载后,运算性能提高不明显,因为主要的运算时间在A3-A6,这部分使用不上优化方法,用优化方法的结果集只有100行数据,优化效果体现不出来。