¿Postgres realizará el cálculo de las columnas calculadas que no están seleccionadas en una vista?

8

Estoy tratando de comprender el impacto en el rendimiento de seleccionar datos de una vista, donde una de las columnas de una vista es función de otros datos en la tabla original.

¿El cálculo se realiza independientemente de si la columna calculada está o no en la lista de columnas seleccionadas?

Si tuviera una mesa y la vista así se declarara

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

¿Se realizaría esa multiplicación en una consulta como la siguiente?

select ticker, ddate, price, factor from prices

¿Hay alguna referencia que garantice de una forma u otra? Estaba leyendo la documentación sobre el sistema de reglas en Postgres, pero creo que la respuesta realmente recae en el optimizador, ya que nada en la documentación del sistema de reglas indicaba que no se seleccionaría.

Sospecho que en el caso anterior no se realiza el cálculo. Cambié la vista para usar la división en lugar de la multiplicación, e inserté un 0for factoren price_data. La consulta anterior no falló, pero si la consulta se modificó para seleccionar la columna calculada, la consulta modificada falló.

¿Hay alguna manera de entender qué cálculos se están haciendo cuando selectse lleva a cabo una? Supongo que estoy buscando algo así, EXPLAINpero que también me informa sobre los cálculos que se están realizando.

Varun Madiath
fuente
1
Esta es una excelente pregunta del tipo que queremos alentar en este SE
Gaius

Respuestas:

6

Como dijo @Laurenz, su análisis es correcto: el optimizador evitará evaluar expresiones de columna que no afecten el resultado de la consulta (y su intento de forzar un error de división por cero es prueba de ello).

Esto depende de las columnas que esté seleccionando, pero también depende de la categoría de volatilidad de las expresiones de columna. El optimizador es libre de omitir immutabley stablellamar a las funciones si su salida nunca se usa, ya que no pueden afectar el resultado, pero las volatilefunciones pueden tener efectos secundarios, por lo que no se optimizan tan fácilmente.

Por ejemplo:

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 solo volatilese selecciona la columna:

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

... entonces, como puede ver, stable_function()está ausente de la explainsalida, y la falta de un NOTICEconfirma que esta llamada ha sido optimizada.

Sin embargo, si stablese selecciona la columna en su lugar:

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

... luego vemos que ambas expresiones de columna aparecen en el plan, y la NOTICEs muestra que ambas funciones se han ejecutado.

No parece haber ninguna mención explícita de este comportamiento en los documentos, por lo que no hay garantías firmes de si se evaluará o no una expresión, y no debe confiar en los efectos secundarios que puedan tener sus llamadas de función.

Pero si su única preocupación es el rendimiento, a continuación, todo el tiempo que marca sus funciones stableo immutableen su caso, puede estar razonablemente seguro (especialmente en los casos simples como este) que no van a ser evaluados si no son necesarios.

(Y mientras esté allí auditando sus declaraciones de volatilidad, es posible que también desee establecer las banderas de seguridad paralelas ).

Nick Barnes
fuente
1
"por lo que se llamará independientemente" Se llamará de acuerdo con las garantías particulares del DBMS. De los cuales puede no haber ninguno. Una consulta SQL describe un resultado, no un proceso. Documentación de PostgesSQL sobre VOLÁTIL: El optimizador no hace suposiciones sobre el comportamiento de tales funciones. Una consulta que utiliza una función volátil volverá a evaluar la función en cada fila donde se necesita su valor. (Cualquiera que sea "necesario" medios.)
philipxy
@philipxy: Tienes toda la razón. No quise dar a entender ninguna garantía más allá de las indicadas en los documentos, aunque en relectura, ciertamente lo hice. Espero que mi edición aclare las cosas. ¡Gracias!
Nick Barnes
4

Su sospecha es correcta, y el cálculo no debe realizarse si no se utiliza la columna.

Para confirmar eso, mire el resultado de EXPLAIN (VERBOSE)la consulta, que le mostrará las columnas devueltas.

Laurenz Albe
fuente