用 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的连接运算,而我们知道lineitem是orders的子表,也是按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@q与customer表有序连接。
三、 进一步优化
1. 优化方法
本例中要使用Q2中介绍的维表主键序号化方法,customer中的C_CUSTKEY、orders中的O_CUSTKEY都已在之前的例子中转换过了。
2. 数据转换代码
复制customer_13.ctx、orders_13.ctx、lineitem_17.ctx分别重命名为customer_18.ctx、orders_18.ctx、lineitem_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行数据,优化效果体现不出来。
英文版