Nous importons des taux de change dans 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);
mais en réalité, nous n'avons besoin que du dernier taux disponible pour travailler.
Il est lourde d'écrire CTE avec trier et distinct on (currency)
:
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
...
La requête a suivi le plan d'exécution c'est bien:
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)
J'ai créé la vue:
CREATE OR REPLACE VIEW latest_currency_rate AS
SELECT
DISTINCT ON (currency) currency, rate, date
FROM currency_rate
ORDER BY currency, date DESC;
mais DB Optimizer n'utilise pas d'index de 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)
et même pour:
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)
L'intégration de la nouvelle vue à la requête originale donne:
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)
...
Maintenant je suis perplexe. Pourquoi la requête CTE d'origine utilise-t-elle Index Scan
Et la vue n'utilise pas le même index?
Devrais-je réécrire la vue avec une autre astuce (au lieu de distinct on
)?
Je pense à aller avec materialized view
Pour éviter les scans séquentiels ...
Votre point de vue donne la bonne réponse, tandis que votre CTE donne la mauvaise réponse, en utilisant la date la plus ancienne, pas la plus récente.
Si vous souhaitez utiliser un scan d'index (bien que dans mes mains, cela ne fait pas une différence de performance), spécifiez DESC
pour les colonnes ou créez un index pour (currency, date DESC)
.