0

次の構造のテーブルがあります

|user_id | place | type_of_place | money_earned| time |
|--------+-------+---------------+-------------+------|
|        |       |               |             |      |

テーブルは非常に大きく、数百万行あります。データは PostgreSQL 9.1 データベースにあります。

user_id と type_of_place ごとに、平均、標準偏差、上位 5 位 (カウント順)、および最も使用された時間 (モード) を計算したいと思います。

結果のデータは次の形式である必要があります。

| user_id | type_of_place | avg | stddev |   top5_places    | mode |
+---------+---------------+-----+--------+------------------+------+
|     1   |      tp1      | 10  |   1    | {p1,p2,p3,p4,p5} |   8  |
|     2   |      tp1      |  3  |   2    | {p3,p4}          |   23 |
|     1   |      tp3      |  1  |   1    | {p1}             |   4  |

ウィンドウ関数でこれを効率的に行うための方法はありますか?

週ごとにグループ化したい場合はどうすればよいですか? (つまり、週数を表す別の列)

ありがとうございました!

4

1 に答える 1

1

標準の GROUP BY クエリは、ほとんどの方法で取得できます。

SELECT
    user_id,
    type_of_place,
    avg(money_earned) AS avg,
    stddev(money_earned) AS stddev
FROM
    earnings  -- I'm not sure what your data table is called...
GROUP BY
    user_id,
    type_of_place

これにより、列top5_placesmode列が残ります。これらは両方とも集約ですが、標準の PostgreSQL インストールで定義されているものではありません。幸いなことに、それらを追加できます。

mode集計関数 を定義する方法について説明しているページは次のとおりです: http://wiki.postgresql.org/wiki/Aggregate_Mode

何らかのタイムスタンプであるmodeと仮定して集計関数を作成したら、選択リストに追加する式は次のようになります。time

SELECT
    ...
    mode(extract(hour FROM time)) AS mode  -- Add this expression
FROM
    ...

お金による注文を想定

の場合top5_places、いくつかのアプローチがありますが、最も速いのは、おそらく PostgreSQL の組み込みarray_agg関数を使用して、最初の 5 つの要素を取得することです。

SELECT
    ...
    (array_agg(place ORDER BY money_earned DESC))[1:5] AS top5_places  -- Add this expression
FROM
    ...

top51 つの代替方法は、同じ機能を実行する(たとえば) という別の集約を定義することです。ユーザー/場所の組み合わせごとに異なる場所が多数ある場合、最初の 5 つを超えると蓄積が停止する可能性があるため、これはより効率的です。一方、上記の式は通常、すべての場所の完全な配列を構築し、最初の場所に切り詰めます。 5.

これは、ユーザーとタイプの組み合わせごとに場所に固有の収益エントリがあることを前提としています。 場所が複数回発生する可能性があり、sum(money_earned)場所ごとに並べ替えたい場合は、以下の例のようにサブクエリを使用する必要があります...

カウント順

わかりましたので、場所は発生頻度で並べる必要があります。いくつかのサブクエリを使用する簡単な方法を次に示します。これを式として上記のクエリの select-clause に追加します。

(SELECT
    (array_agg(place ORDER BY cnt DESC))[1:5]
FROM
    (SELECT place, count(*) FROM earnings AS t2
     WHERE t2.user_id = earnings.user_id AND t2.type_of_place = earnings.type_of_place
     GROUP BY place) AS s (place, cnt)
) AS top5_places

呼び出された内側のサブクエリは、そのユーザー/タイプの組み合わせsのそれぞれのテーブルplaceと、それが発生した回数 (私が呼び出したものcnt) に評価されます。これらはarray_agg、そのカウントの降順で供給されます。

もっときちんとした (そしておそらくもっと効率的な) 書き方があるのではないかと思います。そうでない場合は、可能であれば、この複雑な式を関数または集計に移動することをお勧めします...

時間ごとの場所のヒストグラム

同様の式を使用すると、時間順に並べられたカウントの配列が返されます。

(SELECT
    array_agg(cnt ORDER BY hour DESC)
FROM
    (SELECT extract(hour FROM time), count(*) FROM earnings AS t2
     WHERE t2.user_id = earnings.user_id AND t2.type_of_place = earnings.type_of_place
     GROUP BY 1) AS s (hour, cnt)
) AS hourly_histogram

(それを元のクエリの select-clause に追加します。)

于 2012-11-13T01:32:47.503 に答える