性能优化案例课程 TPCH-Q10

select * from (
    select
        c_custkey,c_name,
        sum(l_extendedprice * (1 - l_discount)) as revenue,
        c_acctbal,n_name,c_address,c_phone,c_comment
    from
        customer,orders,lineitem,nation
    where
        c_custkey = o_custkey
        and l_orderkey = o_orderkey
        and o_orderdate >= date '1993-05-01'
        and o_orderdate < date '1993-05-01' + interval '3' month
        and l_returnflag = 'R'
        and c_nationkey = n_nationkey
    group by
        c_custkey,
        c_name,
        c_acctbal,
        c_phone,
        n_name,
        c_address,
        c_comment
    order by
        revenue desc
) where rownum <=20;

这是多表关联(其中包括主子表 orders 和 lineitem 关联)过滤后的分组聚合运算,然后按分组聚合值排序后取前 20 条记录。

1. 数据存储

大表 orders、lineitem 按照主键 orderkey 有序存储,连接的时候可以有序归并。其他表没有特殊的排序要求,就按照主键有序存储。

这样,就可以继续使用题目 Q3 中的 orders.ctx、lineitem.ctx、customer.ctx。

nation 表直接使用题目 Q2 的 nation.btx。

将这些表复制到本题的主目录中。

2. 一般实现


A

1

=now()

2

1993-5-1

3

=elapse@m(A2,3)

4

=file("nation.btx").import@b().keys@i(N_NATIONKEY)

5

=file("customer.ctx").open().cursor@m(C_CUSTKEY,C_NAME,C_ADDRESS,C_NATIONKEY,C_PHONE,C_ACCTBAL,C_COMMENT;C_NATIONKEY:A4).fetch().keys@i(C_CUSTKEY)

6

=file("orders.ctx").open().cursor@m(O_ORDERKEY,O_CUSTKEY;O_ORDERDATE>=A2 && O_ORDERDATE<A3,O_CUSTKEY:A5)

7

=file("lineitem.ctx").open().news@r(A6,sum(L_EXTENDEDPRICE*(1-L_DISCOUNT)):dp,O_CUSTKEY;L_RETURNFLAG=="R")

8

=A7.groups@u(O_CUSTKEY:c_custkey;sum(dp):revenue)

9

=A8.top(-20;revenue)

10

=A9.new(c_custkey.C_CUSTKEY,c_custkey.C_NAME,revenue,c_custkey.C_ACCTBAL,c_custkey.C_NATIONKEY.N_NAME,c_custkey.C_ADDRESS,c_custkey.C_PHONE,c_custkey.C_COMMENT)

11

=A10.sort@z(revenue)

12

=interval@ms(A1,now())

代码中用到了前面讲到过的游标前过滤、主子表有序归并、外键字段属性化等优化方法。

测试结果:

测试项目

执行时间(秒)

一般实现

17

3. 大维表查找

我们发现,这个运算可以改造成两步,第一步把 orders 和 lineitem 表关联,完成针对 custkey 的分组汇总运算,然后再基于这个结果集继续和维表 customer 做关联运算。用 SQL 大致可以改写成这样:

select c_custkey,c_name,revenue,c_acctbal,n_name,c_address,c_phone,c_comment
from (
    select o_custkey, revenue
    from (
    	select o_custkey,
       	    sum(l_extendedprice * (1 - l_discount)) as revenue
    	from
            orders,lineitem
    	where
       	    l_orderkey = o_orderkey
        	    and o_orderdate >= date '1993-05-01'
        	    and o_orderdate < date '1993-05-01' + interval '3' month
        	    and l_returnflag = 'R'
            group by o_custkey
            order by revenue desc
    ) orders_lineitem 
    where rownum <=20
) orders_lineitem20,customer,nation
where
    c_custkey = o_custkey
    and c_nationkey = n_nationkey
order by revenue desc;

因为最终结果只要取 20 条记录,子查询 orders_lineitem20 也只要返回 20 条记录,然后用这 20 条记录和 cutstomer 表做后续的关联计算即可,而原来的方法相当于子查询 orders_lineitem 的每一条记录都要和 customer 表关联。

这是个小事实表 orders_lineitem20 和大维表 customer 连接的场景,可以采用大维表查找的方法,将连接转换为对 customer 表主键的批量查找。customer 表对主键 c_custkey 有序,可以快速的完成查找计算。

计算代码:


A

1

=now()

2

1993-5-1

3

=elapse@m(A2,3)

4

=file("orders.ctx").open().cursor@m(O_ORDERKEY,O_CUSTKEY;O_ORDERDATE>=A2 && O_ORDERDATE<A3)

5

=file("lineitem.ctx").open().news@r(A4,sum(L_EXTENDEDPRICE*(1-L_DISCOUNT)):dp,O_CUSTKEY;L_RETURNFLAG=="R")

6

=A5.groups@u(O_CUSTKEY:c_custkey;sum(dp):revenue)

7

=A6.top(-20;revenue)

8

=file("nation.btx").import@b().keys@i(N_NATIONKEY)

9

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

10

=A7.joinx@q(c_custkey,A9:C_CUSTKEY,C_NAME,C_ADDRESS,C_NATIONKEY,C_PHONE,C_ACCTBAL,C_COMMENT)

11

=A10.switch(C_NATIONKEY,A8:N_NATIONKEY)

12

=A11.new(c_custkey:C_CUSTKEY,C_NAME,revenue,C_ACCTBAL,C_NATIONKEY.N_NAME,C_ADDRESS,C_PHONE,C_COMMENT)

13

=A12.sort@z(revenue)

14

=interval@ms(A1,now())

A7 算出来之后,用 joinx@q 从 customer 表中有序匹配快速地把相关记录取出来,不必将 customer 装入内存,也不必用 orders 表中的大量数据去连接 customer 表,减少数据读取量、计算量。

测试结果:

测试项目

执行时间(秒)

一般实现

17

大维表查找

7

4. 数据变换

数据变换要采用前面题目提到的枚举型字符串字段数字化、维表主键序号化、日期整数化手段。

nation 表,可以直接使用以前的题目 Q2 转换的 nation_2.btx。

customer 使用 Q5 中的 customer_3.ctx。

orders 表使用 Q3 中的 orders_5.ctx。

将这些表复制到本题的主目录中。

Q1 虽然将 lineitem 中的 L_RETURNFLAG 转化成数字了,但 lineitem.ctx 没有主键,不满足有序归并的要求。需要在 Q3 的 lineitem.ctx 基础上做枚举字段数字化转换。

数据转换代码:


A

1

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

2

=A1.cursor(L_RETURNFLAG).id(L_RETURNFLAG).sort()

3

=file("l_returnflag.btx").export@b(A2)

4

=A1.cursor().run(L_RETURNFLAG =A2.pos@b(L_RETURNFLAG))

5

=file("lineitem4.ctx").create(#L_ORDERKEY,#L_LINENUMBER,L_PARTKEY,L_SUPPKEY,L_QUANTITY,L_EXTENDEDPRICE,L_DISCOUNT,L_TAX,L_RETURNFLAG,L_LINESTATUS,L_SHIPDATE,L_COMMITDATE,L_RECEIPTDATE,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT)

6

>A5.append(A4)

计算代码:


A

1

=now()

2

1993-5-1

3

=days@o(elapse@m(A2,3))

4

=days@o(A2)

5

=file("nation_2.btx").import@b()

6

=file("l_returnflag.btx").import@b().(_1=="R")

7

=file("orders_5.ctx").open().cursor@m(O_ORDERKEY,O_CUSTKEY;O_ORDERDATE>=A4 && O_ORDERDATE<A3)

8

=file("lineitem4.ctx").open().news@r(A7,sum(L_EXTENDEDPRICE*(1-L_DISCOUNT)):dp,O_CUSTKEY;A6(L_RETURNFLAG))

9

=A8.groups@u(O_CUSTKEY:c_custkey;sum(dp):revenue)

10

=A9.top(-20;revenue)

11

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

12

=A10.joinx@q(c_custkey,A11:C_CUSTKEY,C_NAME,C_ADDRESS,C_NATIONKEY,C_PHONE,C_ACCTBAL,C_COMMENT)

13

=A12.new(c_custkey:C_CUSTKEY,C_NAME,revenue,C_ACCTBAL,A5(C_NATIONKEY).N_NAME,C_ADDRESS,C_PHONE,C_COMMENT)

14

=A13.sort@z(revenue)

15

=interval@ms(A1,now())

A3、A4 是日期数字化,A6 是对位序列。

A13 的 nation 表使用了外键字段序号化。

测试结果:

测试项目

执行时间(秒)

一般实现

17

大维表查找

7

数据变换

5

5. 列式计算


A

1

=now()

2

1993-5-1

3

=days@o(elapse@m(A2,3))

4

=days@o(A2)

5

=file("nation_2.btx").import@b()

6

=file("l_returnflag.btx").import@b().(_1=="R")

7

=file("orders_5.ctx").open().cursor@mv(O_ORDERKEY,O_CUSTKEY;O_ORDERDATE>=A4 && O_ORDERDATE<A3)

8

=file("lineitem4.ctx").open().news@r(A7,sum(L_EXTENDEDPRICE*(1-L_DISCOUNT)):dp,O_CUSTKEY;A6(L_RETURNFLAG))

9

=A8.groups@u(O_CUSTKEY:c_custkey;sum(dp):revenue)

10

=A9.top(-20;revenue)

11

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

12

=A10.joinx@q(c_custkey,A11:C_CUSTKEY,C_NAME,C_ADDRESS,C_NATIONKEY,C_PHONE,C_ACCTBAL,C_COMMENT)

13

=A12.new(c_custkey:C_CUSTKEY,C_NAME,revenue,C_ACCTBAL,A5(C_NATIONKEY).N_NAME,C_ADDRESS,C_PHONE,C_COMMENT)

14

=A13.sort@z(revenue)

15

=interval@ms(A1,now())

测试结果:

测试项目

执行时间(秒)

一般实现

17

大维表查找

7

数据变换

5

列式计算

3