Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

218
Vistas
Why postgres virtual columns break pull up condition and forces Subquery Scan?

I've try implement expression indexing on virtual columns as Bruce Momjian suggest in blog.

It work as expected (but require additional data in table to became index useful). Then I've try use it in view and it break it!

Step by step:

CREATE TABLE customer (id SERIAL, firstname TEXT, lastname TEXT);

-- create virtual column
CREATE FUNCTION fullname(customer) RETURNS text AS $$
    SELECT $1.firstname || ' ' || $1.lastname
$$ LANGUAGE SQL;

INSERT INTO customer VALUES (DEFAULT, 'Mark', 'Pennypincher');

-- Insert some data for index became useful
INSERT INTO customer(firstname, lastname)
SELECT 'first_' || n, 'last_' || n FROM generate_series(1, 1000) as n;

CREATE INDEX i_customer_firstname ON customer (firstname);
CREATE INDEX i_customer_bothnames ON customer ((firstname || ' ' || lastname));
CREATE INDEX i_customer_fullname ON customer (fullname(customer));

ANALYZE customer;

EXPLAIN SELECT * FROM customer WHERE customer.fullname = 'Mark Pennypincher';

For now it work as expected, query use index:

 Index Scan using i_customer_bothnames on customer  (cost=0.28..8.29 rows=1 width=21) 
   Index Cond: (((firstname || ' '::text) || lastname) = 'Mark Pennypincher'::text)

Now create view:

CREATE OR REPLACE VIEW v_customer AS
    SELECT
        c.*
        ,c.fullname
    FROM customer c;

We have no aggregations and limits, so according to the documentation thanks to rewrite mechanism and optimizer it should be pulled up in the query. But it is not!

Look:

EXPLAIN SELECT * FROM v_customer WHERE v_customer.fullname = 'Mark Pennypincher';

immediately leads to next query plan:

 Subquery Scan on v_customer  (cost=0.00..34.53 rows=5 width=53)     
   Filter: (v_customer.fullname = 'Mark Pennypincher'::text)         
   ->  Seq Scan on customer c  (cost=0.00..22.01 rows=1001 width=53)

Why Subquery Scan??

What also interesting next two statements:

EXPLAIN SELECT * FROM v_customer WHERE v_customer.firstname = 'Mark';

EXPLAIN SELECT * FROM v_customer WHERE v_customer.firstname || ' ' || v_customer.lastname = 'Mark Pennypincher';

both use their indexes also.

Off course I have more complicated view in reality with several joins. There just example of problem. And it is the reason why I want replace it by view for simplicity.

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

You need to make the function immutable, otherwise the planner won't use it:

CREATE FUNCTION fullname(customer) RETURNS text 
AS 
$$
    SELECT $1.firstname || ' ' || $1.lastname
$$ 
LANGUAGE SQL 
immutable; --<< here

This then generates the following plan with your sample data:

Index Scan using i_customer_fullname on stuff.customer  (cost=0.28..8.29 rows=1 width=21) (actual time=0.054..0.054 rows=1 loops=1)
  Output: id, firstname, lastname                                                                                                  
  Index Cond: (((customer.firstname || ' '::text) || customer.lastname) = 'Mark Pennypincher'::text)                               
  Buffers: shared hit=1 read=2                                                                                                     
  I/O Timings: read=0.035                                                                                                          
Planning time: 0.571 ms                                                                                                            
Execution time: 0.079 ms                                                                                                           

If the function is not immutable, Postgres can't rely on the values that are stored in the index to be always the same. stable would have achieved the same goal. But as the function only relies on values passed to it, immutable is the correct choice.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda