Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

222
Visualizações
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 Respostas
Responde à pergunta

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda