性能优化案例课程 TPCH-Q12

select
    l_shipmode,
    sum(case
        when o_orderpriority = '1-URGENT' or o_orderpriority = '2-HIGH' then 1
        else 0 end) as high_line_count,
    sum(case
        when o_orderpriority <> '1-URGENT' and o_orderpriority <> '2-HIGH' then 1
        else 0 end) as low_line_count
from
    orders,
    lineitem
where
    o_orderkey = l_orderkey
    and l_shipmode in ('TRUCK', 'MAIL')
    and l_commitdate < l_receiptdate
    and l_shipdate < l_commitdate
    and l_receiptdate >= date '1995-01-01'
    and l_receiptdate < date '1995-01-01' + interval '1' year
group by
    l_shipmode
order by
    l_shipmode;

这是主表 orders 与子表 lineitem 关联过滤后的分组聚合计算。

1. 数据存储

大表 orders、lineitem 按照主键 orderkey 有序存储,连接的时候可以有序归并。

继续使用题目 Q3 中的 orders.ctx、lineitem.ctx。

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

2. 一般实现


A

1

=now()

2

1995-1-1

3

=elapse@y(A2, 1)

4

=["MAIL", "TRUCK"]

5

=file("lineitem.ctx").open().cursor@m(L_ORDERKEY,L_SHIPMODE;L_RECEIPTDATE>=A2 && L_RECEIPTDATE<A3 && A4.contain(L_SHIPMODE) && L_COMMITDATE<L_RECEIPTDATE && L_SHIPDATE<L_COMMITDATE)

6

=file("orders.ctx").open().new@r(A5,L_SHIPMODE,O_ORDERPRIORITY)

7

=A6.run(O_ORDERPRIORITY=if(O_ORDERPRIORITY=="1-URGENT" || O_ORDERPRIORITY=="2-HIGH",1,0))

8

=A7.groups(L_SHIPMODE;sum(O_ORDERPRIORITY):high_line_count, sum(1-O_ORDERPRIORITY):low_line_count)

9

=interval@ms(A1,now())

代码中用到了前面讲到过的游标前过滤、多线程并行等优化方法。

high_line_count 和 low_line_count 的条件正好相反,在没有求和的时候,前者是 1 时后者是 0,前者是 0 时后者是 1,所以 A7、A8 中用 O_ORDERPRIORITY 和 1- O_ORDERPRIORITY 来计算求和之前的这两个值。

测试结果:

测试项目

执行时间(秒)

一般实现

6

3. 数据变换

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

可以直接使用以前的题目 Q4 转换的 orders_4.ctx。

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

Lineitem 要在 Q3 的基础上,对枚举型字符串字段 l_shipmod 完成数字化。

数据转换代码:


A

1

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

2

=A1.cursor(L_SHIPMODE)

3

=A2.id(L_SHIPMODE).sort()

4

=file("l_shipmode.btx").export@b(A3)

5

=A1.cursor()

6

=A5.run(L_SHIPMODE=A3.pos@b(L_SHIPMODE),L_RECEIPTDATE=days@o(L_RECEIPTDATE),L_COMMITDATE=days@o(L_COMMITDATE),L_SHIPDATE=days@o(L_SHIPDATE))

7

=file("lineitem_3.ctx").create@py(#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)

8

>A7.append(A6)

计算代码:


A

1

=now()

2

>l_shipmode=file("l_shipmode.btx").import@b().(_1)

3

>o_orderpriority=file("o_orderpriority.btx").import@b().(_1)

4

1995-1-1

5

=days@o(elapse@y(A4, 1))

6

=days@o(A4)

7

=l_shipmode.(["MAIL", "TRUCK"].contain(~))

8

=o_orderpriority.(["1-URGENT","2-HIGH"].contain(~))

9

=file("lineitem_3.ctx").open().cursor@m(L_ORDERKEY,L_SHIPMODE;L_RECEIPTDATE>=A6 && L_RECEIPTDATE<A5 && A7(L_SHIPMODE) && L_COMMITDATE<L_RECEIPTDATE && L_SHIPDATE<L_COMMITDATE)

10

=file("orders_4.ctx").open().new@r(A9,L_SHIPMODE,O_ORDERPRIORITY)

11

=A10.run(O_ORDERPRIORITY=A8(O_ORDERPRIORITY))

12

=A11.groups(L_SHIPMODE;count(O_ORDERPRIORITY):high_line_count, count(!O_ORDERPRIORITY):low_line_count)

13

=A12.run(L_SHIPMODE=l_shipmode(L_SHIPMODE))

14

=interval@ms(A1,now())

A7、A8 是对位序列。

A11 中的 A8(O_ORDERPRIORITY) 返回的是 true 或者 false,对应原来的 1、0,A12 中的 count 和原来的 sum 结果相同。这就省去了 if 判断,可以提高性能。

测试结果:

测试项目

执行时间(秒)

一般实现

6

数据变换

4

4. 列式计算


A

1

=now()

2

>l_shipmode=file("l_shipmode.btx").import@b().(_1)

3

>o_orderpriority=file("o_orderpriority.btx").import@b().(_1)

4

1995-1-1

5

=days@o(elapse@y(A4, 1))

6

=days@o(A4)

7

=l_shipmode.(["MAIL", "TRUCK"].contain(~))

8

=o_orderpriority.(["1-URGENT","2-HIGH"].contain(~))

9

=file("lineitem_3.ctx").open().cursor@mv(L_ORDERKEY,L_SHIPMODE;L_RECEIPTDATE>=A6 && L_RECEIPTDATE<A5 && A7(L_SHIPMODE) && L_COMMITDATE<L_RECEIPTDATE && L_SHIPDATE<L_COMMITDATE)

10

=file("orders_4.ctx").open().new@r(A9,L_SHIPMODE,O_ORDERPRIORITY)

11

=A10.derive@o(A8(O_ORDERPRIORITY):flag)

12

=A11.groups(L_SHIPMODE;count(flag):high_line_count,count(!flag):low_line_count)

13

=A12.new(l_shipmode(L_SHIPMODE):L_SHIPMODE,high_line_count,low_line_count)

14

=interval@ms(A1,now())

列式计算不能给字段赋值,所以 A11 中新定义了一个字段 flag。

测试结果:

测试项目

执行时间(秒)

一般实现

6

数据变换

4

列式计算

2