web-dev-qa-db-ja.com

グループの最大値を持つ行を取得するためのDISTINCT ONとROW_NUMBER()

PostgreSQL 9.4には次のテーブルがあります。

_CREATE TABLE dpa(
  id serial NOT NULL,
  currency_id integer,
  amount numeric(14,3),
  date timestamp without time zone,
  plat_id integer,
  pl_id integer,
  p_id integer,
  CONSTRAINT dpa_pkey PRIMARY KEY (id), 
)
_

と設定:

_work_mem = 128MB
table_size = 16 MB
_

そしてインデックス:

_CREATE INDEX idx1
  ON dpa
  USING btree
  (plat_id, p_id, pl_id, currency_id, date DESC NULLS LAST, amount)
_

テーブルは約_242K_行で構成されています。列に_NOT NULL_制約はありませんが、実際には_NOT NULL_です。

ここで、クエリのパフォーマンスを測定しています。

_SELECT plat_id, p_id, pl_id, player_account player_account
FROM(
    SELECT plat_id, p_id, pl_id, 
    COALESCE(amount, 0) player_account,
    ROW_NUMBER() OVER (PARTITION BY plat_id, p_id, pl_id, currency_id
                       ORDER BY date DESC NULLS LAST) rn
    FROM dpa
) sub WHERE rn = 1;
_

分析された計画:

_Subquery Scan on sub  (cost=0.42..25484.16 rows=1214 width=44) (actual time=0.044..296.810 rows=215274 loops=1)
  Filter: (sub.rn = 1)
  Rows Removed by Filter: 27556
  ->  WindowAgg  (cost=0.42..22448.79 rows=242830 width=28) (actual time=0.043..255.690 rows=242830 loops=1)
        ->  Index Only Scan using idx1 on dpa  (cost=0.42..16378.04 rows=242830 width=28) (actual time=0.037..91.576 rows=242830 loops=1)"
              Heap Fetches: 242830
_

II

_SELECT DISTINCT ON(plat_id, p_id, pl_id, currency_id)
       plat_id, p_id, pl_id, currency_id, amount
FROM dpa
ORDER BY plat_id, p_id, pl_id, currency_id, date DESC NULLS LAST
_

分析された計画:

_Unique  (cost=0.42..18794.73 rows=82273 width=28) (actual time=0.017..128.277 rows=215274 loops=1)
  ->  Index Only Scan using idx1 on dpa  (cost=0.42..16366.43 rows=242830 width=28) (actual time=0.016..72.110 rows=242830 loops=1)
        Heap Fetches: 242830
_

ご覧のとおり、2番目のクエリは最初のクエリよりも高速です。しかし、PGAdminでこのクエリを実行すると、次の平均統計が得られました。

ROW_NUMBER()(最初の)を使用したクエリ:_4999 ms_

_DISTINCT ON_(2番目)を使用したクエリ:_5654 ms_

このような大きな結果セットに対するbandwith/latencyオーバーヘッドが重要であることを理解しています。すべてのクエリは_215274_行を生成します。

QUESTION:プランナーが2番目のケースですべての行を受信するのに最初のケースよりも時間がかかるのはなぜですか? 2番目の計画がより最適であることを示していますか?

4
St.Antario

表示されるタイミングはpgAdminによって指定されます(ただし、他のクライアントの場合もあります)。つまり、出力にgetおよびrenderに必要な時間を表示します。ご存じのとおり、データベースがデータを生成するために必要な時間(EXPLAIN ANALYZE)、表示される違いはtransferおよび/またはレンダリングに由来する必要があります。たとえば、最初のクエリで表示される列が少なくなったことが原因である可能性があります。

転送中にどれだけの時間が消費されるかを知りたい場合は、アプリケーションからのクエリの実行の時間を計ることができます。データをフェッチするだけで、それをなんらかの方法で処理しない場合(レンダリングすることは言うまでもありません)、データの転送に必要な時間を適切に概算できます。データのフェッチに必要な時間をとり、データベースの(既知の)実行時間を差し引いてください。

このようにして、上記の数値と与えられた数値を比較することにより、pgAdminのレンダリング時間についても把握できます。

1
dezso