6

Postgres 8.4 で約 5 秒で実行されるクエリがあります。他のテーブルに結合されたビューからデータを選択しますが、lag()ウィンドウ関数も使用します。

SELECT *, lag(column1) OVER (PARTITION BY key1 ORDER BY ...), lag(...)
FROM view1 v
JOIN othertables USING (...)
WHERE ...

便宜上、単純な新しいビューを作成しました

SELECT *, lag(column1) OVER (PARTITION BY key1 ORDER BY ...), lag(...)
FROM view1 v

そして、それから SELECT を行い、以前と同様に他のすべての JOIN とフィルターを使用します。驚いたことに、このクエリは 12 分以内に完了しません (その時点で停止しました)。明らかに、Postgres は別の実行計画を選択しました。そうしないようにするにはどうすればよいですか。元のクエリと同じプランを使用しますか? ビューが実行計画を変更するべきではないと私は思っていたでしょうが、どうやらそうです。

編集:さらに、最初のビューの内容を2番目のビューにコピーしても、まだ返されないことがわかりました。

編集 2: OK、計画を投稿するのに十分なほどクエリを単純化しました。

ビューの使用 (これは妥当な時間内に返されません):

Subquery Scan sp  (cost=5415201.23..5892463.97 rows=88382 width=370)
  Filter: (((sp.ticker)::text ~~ 'Some Ticker'::text) AND (sp.price_date >= '2010-06-01'::date))
  ->  WindowAgg  (cost=5415201.23..5680347.20 rows=53029193 width=129)
        ->  Sort  (cost=5415201.23..5441715.83 rows=53029193 width=129)
              Sort Key: sp.stock_id, sp.price_date
              ->  Hash Join  (cost=847.87..1465139.61 rows=53029193 width=129)
                    Hash Cond: (sp.stock_id = s.stock_id)
                    ->  Seq Scan on stock_prices sp  (cost=0.00..1079829.20 rows=53029401 width=115)
                    ->  Hash  (cost=744.56..744.56 rows=29519 width=18)
                          ->  Seq Scan on stocks s  (cost=0.00..744.56 rows=29519 width=18)

ビューからウィンドウ関数を取り出し、クエリ自体に入れます (これはすぐに返されます)。

WindowAgg  (cost=34.91..34.95 rows=7 width=129)
  ->  Sort  (cost=34.91..34.92 rows=7 width=129)
        Sort Key: sp.stock_id, sp.price_date
        ->  Nested Loop  (cost=0.00..34.89 rows=7 width=129)
              ->  Index Scan using stocks_ticker_unique on stocks s  (cost=0.00..4.06 rows=1 width=18)
                    Index Cond: ((ticker)::text = 'Some Ticker'::text)
                    Filter: ((ticker)::text ~~ 'Some Ticker'::text)
              ->  Index Scan using stock_prices_id_date_idx on stock_prices sp  (cost=0.00..30.79 rows=14 width=115)
                    Index Cond: ((sp.stock_id = s.stock_id) AND (sp.price_date >= '2010-06-01'::date))

したがって、遅いケースでは、最初にすべてのデータにウィンドウ関数を適用してからフィルタリングしようとしているようです。これがおそらく問題です。なぜそうしているのかはわかりませんが。

4

2 に答える 2

2

2 つのプランの違いは、集合体との結合によるものです。これにより、ネストされたループ プランを使用できなくなります。ビューで集計を使用すると、その不利なシナリオに身を置くことになります。

たとえば、これにより、ほとんどの場合、2 つのテーブルでマージまたはハッシュ結合プランが実行され、その後に上位 n の並べ替えが行われます。

select foo.*
from foo
join (select bar.* from bar group by bar.field) as bar on foo.field = bar.field
where ...
order by bar.field
limit 10;
于 2011-05-14T12:56:19.523 に答える
0

おそらく、ビューの代わりにCommon Table Expression (CTE)を使用することを検討できます。ビューを使用するのと同様の方法でクエリをより明確にすることができますが、同じように実行計画に影響を与えるようには見えません。

この質問でも同様の問題があり、ビューの代わりに CTE を使用すると、実行計画がはるかに効率的になりました。

于 2012-03-03T18:43:33.127 に答える