O Postgres executará o cálculo de colunas calculadas que não estão selecionadas em uma exibição?

8

Estou tentando entender o impacto no desempenho da seleção de dados em uma exibição, onde uma das colunas em uma exibição é função de outros dados na tabela original.

O cálculo é executado independentemente de a coluna computada estar ou não na lista de colunas selecionadas?

Se eu tivesse uma mesa e a vista declarasse assim

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

Oxalá que multiplicação ser realizada em uma consulta como a abaixo?

select ticker, ddate, price, factor from prices

Existe uma referência que garanta desta forma ou de outra? Eu estava lendo a documentação do sistema de regras no Postgres, mas acho que a resposta realmente está no otimizador, pois nada na documentação do sistema de regras indicava que ele não seria selecionado.

Suspeito que, no caso acima, o cálculo não seja realizado. Alterei a visualização para usar a divisão em vez da multiplicação e inseri um 0for factorem price_data. A consulta acima não falhou, mas se a consulta foi modificada para selecionar a coluna computada, a consulta modificada falhou.

Existe alguma maneira de entender quais cálculos estão sendo feitos quando a selecté realizado? Acho que estou procurando algo parecido, EXPLAINmas que também me fale sobre os cálculos que estão sendo executados.

Varun Madiath
fonte
11
Esta é uma excelente pergunta do tipo que queremos encorajar neste SE
Gaius

Respostas:

6

Como @Laurenz disse, sua análise está correta: o otimizador evitará avaliar expressões de coluna que não afetam o resultado da consulta (e sua tentativa de forçar um erro de divisão por zero é prova disso).

Isso depende de quais colunas você está selecionando, mas também da categoria de volatilidade das expressões da coluna. O otimizador é livre para omitir immutablee stablechamadas de função se sua saída nunca for usada, pois elas não podem afetar o resultado, mas as volatilefunções podem ter efeitos colaterais, portanto, elas não são tão facilmente otimizadas.

Por exemplo:

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();

Se apenas a volatilecoluna estiver selecionada:

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()

... então, como você pode ver, stable_function()está ausente na explainsaída e a falta de uma NOTICEconfirma que esta chamada foi otimizada.

No entanto, se a stablecoluna estiver selecionada:

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()

... então vemos as duas expressões de coluna aparecendo no plano e os NOTICEs mostram que ambas as funções foram executadas.

Parece não haver nenhuma menção explícita a esse comportamento nos documentos, portanto, não há garantias concretas sobre se uma expressão será avaliada ou não e você não deve confiar nos efeitos colaterais que suas chamadas de função possam ter.

Mas se sua única preocupação é desempenho, desde que você marque suas funções como stableou immutableonde apropriado, você pode estar razoavelmente certo (especialmente em casos simples como este) de que elas não serão avaliadas a menos que sejam necessárias.

(E enquanto você estiver lá auditando suas declarações de volatilidade, também poderá definir os sinalizadores de segurança paralelos .)

Nick Barnes
fonte
11
"assim será chamado independentemente" Será chamado de acordo com as garantias específicas do DBMS. Das quais pode não haver nenhuma. Uma consulta SQL descreve um resultado, não um processo. Documentação do PostgesSQL referente a VOLÁTIL: O otimizador não faz suposições sobre o comportamento de tais funções. Uma consulta usando uma função volátil reavaliará a função em todas as linhas em que seu valor for necessário. (Qualquer que seja o significado "necessário".) #
Philipxy 18/06/19
@ philipxy: Você está absolutamente certo. Não pretendia implicar nenhuma garantia além daquelas declaradas nos documentos, embora, ao reler, eu certamente o tenha feito. Espero que minha edição esclareça as coisas. Obrigado!
Nick Barnes
4

Sua suspeita está correta e o cálculo não deve ser realizado se a coluna não for usada.

Para confirmar isso, observe a saída de EXPLAIN (VERBOSE)para a consulta, que mostrará as colunas retornadas.

Laurenz Albe
fonte