web-dev-qa-db-ja.com

Postgres:最新のレートでビューのインデックス使用を強制するにはどうすればよいですか?

通貨レートをDBにインポートします。

_CREATE TABLE currency_rate (
    id int8 NOT NULL,
    date date NOT NULL,
    currency varchar(3) NOT NULL,
    rate numeric(12,6) NOT NULL,
    CONSTRAINT currency_rate_pk PRIMARY KEY (id)
);

ALTER TABLE currency_rate add constraint currency_rate_un UNIQUE (currency, date);
_

しかし実際には、使用できる最新のレートのみが必要です。

ソートとdistinct on (currency)を使用してCTEを記述するのは面倒です。

_with cr as (
  select distinct on (currency) currency, rate from currency_rate
  order by currency, date)
select
  ...,
  sum((nd.original_amount - nd.new_amount)*cr.rate) as amount
from notification_data nd
join cr on cr.currency = nd.currency
...
_

クエリには次の実行プランがありますつまり、Nice

_  CTE cr
    ->  Result  (cost=0.28..69.66 rows=13 width=16)
          ->  Unique  (cost=0.28..69.66 rows=13 width=16)
                ->  Index Scan using currency_rate_un on currency_rate  (cost=0.28..67.17 rows=995 width=16)
  ...
              ->  Hash Join  (cost=1029.26..57129.68 rows=18 width=60)
                    Hash Cond: ((nd.currency)::text = (cr.currency)::text)
_

私はビューを作成しました:

_CREATE OR REPLACE VIEW latest_currency_rate AS
SELECT
  DISTINCT ON (currency) currency, rate, date
FROM currency_rate
ORDER BY currency, date DESC;
_

しかし、DBオプティマイザーは_currency_rate_un_からのインデックスを使用しません。

_explain select * from latest_currency_rate;

Unique  (cost=60.83..65.38 rows=12 width=16)
  ->  Sort  (cost=60.83..63.10 rows=910 width=16)
        Sort Key: currency_rate.currency, currency_rate.date DESC
        ->  Seq Scan on currency_rate  (cost=0.00..16.10 rows=910 width=16)
_

そしてのためにも:

_explain select * from latest_currency_rate where currency = 'USD';

Unique  (cost=16.87..17.13 rows=12 width=16)
  ->  Sort  (cost=16.87..17.13 rows=104 width=16)
        Sort Key: currency_rate.date DESC
        ->  Bitmap Heap Scan on currency_rate  (cost=5.08..13.38 rows=104 width=16)
              Recheck Cond: ((currency)::text = 'USD'::text)
              ->  Bitmap Index Scan on currency_rate_un  (cost=0.00..5.06 rows=104 width=0)
                    Index Cond: ((currency)::text = 'USD'::text)
_

新しいビューを元のクエリに統合すると、次のようになります。

_explain select
  sum((nd.original_amount - nd.new_amount)*cr.rate) as amount
from notification_data nd
join latest_currency_rate cr on cr.currency = nd.currency
...

...
 ->  Hash  (cost=73.54..73.54 rows=13 width=12)
       ->  Subquery Scan on cr  (cost=68.37..73.54 rows=13 width=12)
             ->  Unique  (cost=68.37..73.41 rows=13 width=16)
                   ->  Sort  (cost=68.37..70.89 rows=1008 width=16)
                         Sort Key: currency_rate.currency, currency_rate.date DESC
                         ->  Seq Scan on currency_rate  (cost=0.00..18.08 rows=1008 width=16)
...
_

今、私は困惑しています。元のCTEクエリが_Index Scan_を使用し、ビューが同じインデックスを使用しないのはなぜですか?

(_distinct on_の代わりに)いくつかの代替トリックでビューを書き換える必要がありますか?

順次スキャンを回避するために_materialized view_を使用することを考えています...

1
gavenkoa

あなたの見解は正しい答えを与えますが、あなたのCTEは間違った答えを与えます。最も新しい日付ではなく最も古い日付を使用します。

インデックススキャンを使用する場合(私の手では実際にはパフォーマンスに違いはありません)、両方のORDER BY列にDESCを指定するか、(currency, date DESC)のインデックスを作成します。

2
jjanes