Postgres effectuera-t-il le calcul des colonnes calculées qui ne sont pas sélectionnées dans une vue?


8

J'essaie de comprendre l'impact sur les performances de la sélection de données dans une vue, où l'une des colonnes d'une vue est fonction d'autres données de la table d'origine.

Le calcul est-il effectué indépendamment du fait que la colonne calculée figure ou non dans la liste des colonnes sélectionnées?

Si j'avais une table et la vue déclarée comme ça

CREATE TABLE price_data (
    ticker     text,          -- Ticker of the stock
    ddate      date,          -- Date for this price
    price      float8,        -- Closing price on this date
    factor     float8         -- Factor to convert this price to USD
);

CREATE VIEW prices AS 
    SELECT ticker, 
           ddate,
           price,
           factor,
           price * factor as price_usd
    FROM price_data

Would que la multiplication être effectuée dans une requête comme celle ci - dessous?

select ticker, ddate, price, factor from prices

Existe-t-il une référence garantissant ceci dans un sens ou dans l'autre? Je lisais la documentation sur le système de règles dans Postgres, mais je pense que la réponse réside vraiment dans l'optimiseur, car rien dans la documentation du système de règles n'indiquait qu'il ne serait pas sélectionné.

Je soupçonne que dans le cas ci-dessus, le calcul n'est pas effectué. J'ai changé la vue pour utiliser la division au lieu de la multiplication et j'ai inséré un 0for factordans price_data. La requête ci-dessus n'a pas échoué, mais si la requête a été modifiée pour sélectionner la colonne calculée, la requête modifiée a échoué.

Existe-t-il un moyen de comprendre quels calculs sont effectués lors de l' selectexécution d'un? Je suppose que je suis à la recherche de quelque chose comme, EXPLAINmais qui me renseigne également sur les calculs qui sont effectués.


1
C'est une excellente question du genre que nous voulons encourager sur ce SE
Gaius

Réponses:


6

Comme l'a dit @Laurenz, votre analyse est correcte: l'optimiseur évitera d'évaluer les expressions de colonne qui n'affectent pas le résultat de la requête (et votre tentative de forcer une erreur de division par zéro en est la preuve).

Cela dépend des colonnes que vous sélectionnez, mais cela dépend également de la catégorie de volatilité des expressions de colonne. L'optimiseur est libre d'omettre immutableet d' stableappeler des fonctions si leur sortie n'est jamais utilisée, car elles ne peuvent pas affecter le résultat, mais les volatilefonctions peuvent avoir des effets secondaires, de sorte qu'elles ne sont pas si facilement optimisées.

Par exemple:

create function stable_function() returns int as $$
begin
  raise notice 'stable_function() called';
  return 1;
end
$$
language plpgsql stable;

create function volatile_function() returns int as $$
begin
  raise notice 'volatile_function() called';
  return 1;
end
$$
language plpgsql volatile;

create view v as
  select stable_function(), volatile_function();

Si seule la volatilecolonne est sélectionnée:

test=# explain (analyse, verbose) select volatile_function from v;
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.27 rows=1 width=4) (actual time=0.057..0.057 rows=1 loops=1)
   Output: v.volatile_function
   ->  Result  (cost=0.00..0.26 rows=1 width=8) (actual time=0.056..0.056 rows=1 loops=1)
         Output: NULL::integer, volatile_function()

... alors comme vous pouvez le voir, stable_function()est absent de la explainsortie, et l'absence d'un NOTICEconfirme que cet appel a été optimisé loin.

Cependant, si la stablecolonne est sélectionnée à la place:

test=# explain (analyse, verbose) select stable_function from v;
NOTICE:  stable_function() called
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.52 rows=1 width=4) (actual time=0.139..0.139 rows=1 loops=1)
   Output: v.stable_function
   ->  Result  (cost=0.00..0.51 rows=1 width=8) (actual time=0.138..0.138 rows=1 loops=1)
         Output: stable_function(), volatile_function()

... alors nous voyons les deux expressions de colonne apparaître dans le plan, et les NOTICEs montrent que les deux fonctions ont été exécutées.

Il ne semble pas y avoir de mention explicite de ce comportement dans les documents, il n'y a donc aucune garantie ferme quant à l'évaluation ou non d'une expression, et vous ne devriez pas vous fier aux effets secondaires que vos appels de fonction pourraient avoir.

Mais si votre seule préoccupation est la performance, tant que vous marquez vos fonctions comme stableou le immutablecas échéant, vous pouvez être raisonnablement sûr (en particulier dans des cas simples comme celui-ci) qu'elles ne seront évaluées que si elles sont nécessaires.

(Et pendant que vous êtes là pour auditer vos déclarations de volatilité, vous pouvez également définir les indicateurs de sécurité parallèles .)


1
"donc il sera appelé indépendamment" Il sera appelé selon les garanties particulières du SGBD. Dont il n'y en a peut-être pas. Une requête SQL décrit un résultat, pas un processus. Documentation PostgesSQL concernant VOLATILE: L'optimiseur ne fait aucune hypothèse sur le comportement de ces fonctions. Une requête utilisant une fonction volatile réévaluera la fonction à chaque ligne où sa valeur est requise. (Tout ce que "nécessaire" signifie.)
philipxy

@philipxy: Vous avez absolument raison. Je ne voulais pas impliquer de garanties au-delà de celles énoncées dans les documents, bien qu'en relisant, je l'ai certainement fait. Espérons que mon montage clarifie les choses. Merci!
Nick Barnes

4

Votre suspicion est correcte et le calcul ne doit pas être effectué si la colonne n'est pas utilisée.

Pour confirmer cela, regardez la sortie de EXPLAIN (VERBOSE)pour la requête, qui vous montrera les colonnes retournées.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.