GraphDBを学ぶ（５）neo4jでTPC-H Query1を実行してみる

# TL;DR

* [前回](https://qiita.com/myoshimi/items/6e8dc3934da6b55d77fe)，neo4jサーバにsf=0.01のデータベースを格納しました
* TPC-Hのsf=0.01のデータベースに対して，Cypherクエリに修正したTPC-H Query1を実行して，ベンチマークします

# TPC-H Query 1の概要

* OLAP向けベンチマーク[TPC-H](http://www.tpc.org/tpch/)が用意している22種類のクエリのうちのうちの1つで，TPC-Hデータベース中のlineitemテーブルから，データを集約して取り出すものです
    * 大規模データの集約性能の計測に使用されるものです

* [TPC-H](http://www.tpc.org/tpch/) Query1のもともとのクエリ(q1.sql)は以下のものです

```sql
select l_returnflag, l_linestatus,
       sum(l_quantity) as sum_qty,
       sum(l_extendedprice) as sum_base_price,
       sum(l_extendedprice*(1-l_discount)) as sum_disc_price,
       sum(l_extendedprice*(1-l_discount)*(1+l_tax)) as sum_charge,
       avg(l_quantity) as avg_qty,
       avg(l_extendedprice) as avg_price,
       avg(l_discount) as avg_disc,
       count(*) as count_order
from lineitem
where l_shipdate <= date '1998-12-01' - interval '[DELTA]' day (3)
group by l_returnflag, l_linestatus
order by l_returnflag, l_linestatus;
```

* クエリの最適化過程で順番が前後するかもしれませんが，処理の概要は以下のとおりです
    * lineitemテーブルを読み出し，処理を行います(```from lineitem```)
    * 条件にあったレコードのみを計算対象にします
        * ```where ```句で，```l_shipdate```がある日時より以前のレコードを対象とします
    * カラムの値ごとに集計します
        * ```l_returnflag```と```l_linestatus```の組み合わせごとに集計します
    * 集計する内容は，いくつかのカラムの値とその集計値です
        * 集計する値は，「価格```l_extendedprice```」と「量```l_quantity```」，「割引率```l_discount```と税金```l_tax```」を使って求められる値の，「総計```sum```」および「平均値```avg```」，「数```count```」です

# Cypherクエリに書き直したTPC-H Query 1

* 前項のTPC-H Query1を，以下のようにCypherクエリで書き換えました

```sql
MATCH(l :lineitem)
    WHERE l.L_SHIPDATE <=
      DATE({ year:1998, month:12, day:1}) - duration('P3D')
    RETURN 
        l.L_RETURNFLAG,
        l.L_LINESTATUS,
        sum(l.L_QUANTITY) AS sum_qty,
        sum(l.L_EXTENDEDPRICE) AS sum_base_price,
        sum(l.L_EXTENDEDPRICE*(1-l.L_DISCOUNT)) AS sum_disc_price,
        sum(l.L_EXTENDEDPRICE*(1-l.L_DISCOUNT)*(1+l.L_TAX)) AS sum_charge, 
        avg(l.L_QUANTITY) as avg_qty, avg(l.L_EXTENDEDPRICE) AS avg_price, 
        avg(l.L_DISCOUNT) as avg_disc, count(*) AS count_order
      ORDER BY l.L_RETURNFLAG, l.L_LINESTATUS;
```

* neo4j上で作成したグラフから，lineitemラベルのノードを取り出します
    * ```MATCH```句でlineitemラベルを取り出し，```l```なる変数名で後続の処理を行います
* 条件にあったレコードを抜き出します
    * ```WHERE```句で，```L_SHIPDATE```が該当日時以前のものを計算対象にします
    * SQLとCypherで，日時条件の表現方法が若干異なります
* 各ノードで行う計算を行います
    * ```RETURN```句以降，SQLと同じです
    * SQLの```Group By```句のような，集計カラムを指定しなくても良いようです
    * [Cypher Document - 4.3. Aggregating functions](https://neo4j.com/docs/cypher-manual/current/functions/aggregating/)
* 表示順を調整します
    * ```ORDER BY```句はSQLと同じです

# neo4j上でTPC-H Query 1を実行

* 前項のCypherクエリを実行した結果を示します

```sql
┌──────────────────┬──────────────────┬─────────────────┬─────────────────────┬─────────────────────┬─────────────────────┬─────────────┬────────────────┬────────────┬──────────────┐
│ l.L_RETURNFLAG   │ l.L_LINESTATUS   │ sum_qty         │ sum_base_price      │ sum_disc_price      │ sum_charge          │ avg_qty     │ avg_price      │ avg_disc   │ count_order  │
╞══════════════════╪══════════════════╪═════════════════╪═════════════════════╪═════════════════════╪═════════════════════╪═════════════╪════════════════╪════════════╪══════════════╡
│ A                │ F                │ 380456.000000   │ 532348211.649998    │ 505822441.486102    │ 526165934.000839    │ 25.575155   │ 35785.709307   │ 0.050081   │ 14876        │
│ N                │ F                │ 8971.000000     │ 12384801.370000     │ 11798257.208000     │ 12282485.056933     │ 25.778736   │ 35588.509684   │ 0.047759   │ 348          │
│ N                │ O                │ 765181.000000   │ 1072769060.239996   │ 1019433269.196703   │ 1060338999.761885   │ 25.466136   │ 35703.033922   │ 0.049928   │ 30047        │
│ R                │ F                │ 381449.000000   │ 534594445.349998    │ 507996454.406699    │ 528524219.358906    │ 25.597168   │ 35874.006533   │ 0.049828   │ 14902        │
└──────────────────┴──────────────────┴─────────────────┴─────────────────────┴─────────────────────┴─────────────────────┴─────────────┴────────────────┴────────────┴──────────────┘
4 rows returned in 811ms (first row after 811ms, rendered after 814ms)
```

* MySQLで実行したQuery 1の実行結果も示します

```sql
+--------------+--------------+-----------+----------------+-----------------+-------------------+-----------+--------------+----------+-------------+
| l_returnflag | l_linestatus | sum_qty   | sum_base_price | sum_disc_price  | sum_charge        | avg_qty   | avg_price    | avg_disc | count_order |
+--------------+--------------+-----------+----------------+-----------------+-------------------+-----------+--------------+----------+-------------+
| A            | F            | 380456.00 |   532348211.65 |  505822441.4861 |  526165934.000839 | 25.575155 | 35785.709307 | 0.050081 |       14876 |
| N            | F            |   8971.00 |    12384801.37 |   11798257.2080 |   12282485.056933 | 25.778736 | 35588.509684 | 0.047759 |         348 |
| N            | O            | 765181.00 |  1072769060.24 | 1019433269.1967 | 1060338999.761883 | 25.466136 | 35703.033922 | 0.049928 |       30047 |
| R            | F            | 381449.00 |   534594445.35 |  507996454.4067 |  528524219.358903 | 25.597168 | 35874.006533 | 0.049828 |       14902 |
+--------------+--------------+-----------+----------------+-----------------+-------------------+-----------+--------------+----------+-------------+
4 rows in set (0.15 sec)
```

* RDBMSとGraphDBで，同じ結果が得られました（丸めで値に若干の差はあります）
* このような方法で，Query1以外のクエリについても実行を検討します

# 参考

* [TPC-H](http://www.tpc.org/tpch/)
* [Cypher Document - 4.3. Aggregating functions](https://neo4j.com/docs/cypher-manual/current/functions/aggregating/)