left join on a view takes significantly more time. - Mailing list pgsql-sql

From Manuel Sugawara
Subject left join on a view takes significantly more time.
Date
Msg-id m3wu50voaq.fsf@conexa.fciencias.unam.mx
Whole thread Raw
Responses Re: left join on a view takes significantly more time.
List pgsql-sql
Hi,

I'm facing a wired problem. When I left join two tables PostgreSQL is
able to do it fast using the corresponding indices, However, if I
define a simple view (to format the data) on one of the tables, the
left join does not use the indices. Is something wrong here?

The two tables:

****************************************
\d regacd.profesor                                         Tabla "regacd.profesor"      Columna        |
Tipo            |                     Modificadores 
----------------------+-----------------------------+-------------------------------------------------------id
        | integer                     | not null default nextval('regacd.profesor_sid'::text)grupo_id             |
integer                    | not nulltipo_id              | "char"                      | not nullcargo_id
|integer                     | not nullacadémico_id         | integer                     |última_actualización |
timestampwithout time zone | default now() 
Índices:   "profesor_pkey" llave primaria, btree (id)   "profesor_académico" btree ("académico_id")   "profesor_grupo"
btree(grupo_id) 
Restricciones de llave foránea:   "CargoTipoId" FOREIGN KEY (cargo_id, tipo_id) REFERENCES cargo(id, tipo_id)
"IdentificadorGrupoTipoId"FOREIGN KEY (grupo_id, tipo_id) REFERENCES regacd.grupo(id, tipo_id)   "$1" FOREIGN KEY
("académico_id")REFERENCES personal(id) 
Triggers:   "profesor_última_actualización" BEFORE INSERT OR UPDATE ON regacd.profesor FOR EACH ROW EXECUTE PROCEDURE
"profesor_última_actualización"()  "propaga_actualización_profesor" AFTER INSERT OR UPDATE ON regacd.profesor FOR EACH
ROWEXECUTE PROCEDURE "propaga_actualización_profesor"()   "update_datos_académico" BEFORE INSERT OR UPDATE ON
regacd.profesorFOR EACH ROW EXECUTE PROCEDURE "update_datos_académico"() 

\d ordinario.horario                                         Tabla "ordinario.horario"      Columna        |
Tipo            |                      Modificadores 
----------------------+-----------------------------+---------------------------------------------------------id
          | integer                     | not null default nextval('ordinario.horario_sid'::text)profesor_id          |
integer                    |lu                   | boolean                     | not null default falsema
   | boolean                     | not null default falsemi                   | boolean                     | not null
defaultfalseju                   | boolean                     | not null default falsevi                   | boolean
                 | not null default falsesá                   | boolean                     | not null default
falsehora_inicial        | time without time zone      |hora_final           | time without time zone      |salón_id
        | integer                     |nota                 | text                        |última_actualización |
timestampwithout time zone | default now() 
Índices:   "horario_pkey" llave primaria, btree (id)   "horario_profesor" btree (profesor_id)
Restricciones de llave foránea:   "$2" FOREIGN KEY ("salón_id") REFERENCES "salón"(id)   "$1" FOREIGN KEY (profesor_id)
REFERENCESregacd.profesor(id) ON UPDATE CASCADE ON DELETE CASCADE 
Triggers:   "horario_última_actualización" BEFORE INSERT OR UPDATE ON ordinario.horario FOR EACH ROW EXECUTE PROCEDURE
"horario_última_actualización"()  "propaga_actualización_horario" AFTER INSERT OR UPDATE ON ordinario.horario FOR EACH
ROWEXECUTE PROCEDURE "propaga_actualización_horario"() 
****************************************

Now, a left join query of the tables leads a nice and fast plan:

explain analyze select * from regacd.profesor p left join ordinario.horario h on (h.profesor_id = p.id) where
p.grupo_idIN (129314, 129315, 129316, 129317, 129318, 129319, 129320, 129321, 129322);
                                                                                         QUERY PLAN

                                                        

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Nested
LoopLeft Join  (cost=0.00..150.79 rows=18 width=78) (actual time=0.400..1.232 rows=19 loops=1)  ->  Index Scan using
profesor_grupo,profesor_grupo, profesor_grupo, profesor_grupo, profesor_grupo, profesor_grupo, profesor_grupo,
profesor_grupo,profesor_grupo on profesor p  (cost=0.00..96.50 rows=18 width=25) (actual time=0.231..0.499 rows=19
loops=1)       Index Cond: ((grupo_id = 129314) OR (grupo_id = 129315) OR (grupo_id = 129316) OR (grupo_id = 129317) OR
(grupo_id= 129318) OR (grupo_id = 129319) OR (grupo_id = 129320) OR (grupo_id = 129321) OR (grupo_id = 129322))  ->
IndexScan using horario_profesor on horario h  (cost=0.00..3.01 rows=1 width=53) (actual time=0.020..0.023 rows=1
loops=19)       Index Cond: (h.profesor_id = "outer".id)Total runtime: 1.542 ms 
(6 filas)

However, if I define a simple view on ordinario.horario

\d vordinario.horario               Vista "vordinario.horario"    Columna      |          Tipo          | Modificadores
------------------+------------------------+---------------horario_id       | integer                |profesor_id
|integer                |lu               | boolean                |ma               | boolean                |mi
       | boolean                |ju               | boolean                |vi               | boolean
|sá              | boolean                |días_txt         | text                   |hora_inicial     | time without
timezone |hora_final       | time without time zone |hora_inicial_txt | text                   |hora_final_txt   | text
                 |salón_id         | integer                |salón_txt        | text                   |horario_nota
| text                   | 
Definición de vista:SELECT h.id AS horario_id, h.profesor_id, h.lu, h.ma, h.mi, h.ju, h.vi, h."sá", "días_atxt"(h.lu,
h.ma,h.mi, h.ju, h.vi, h."sá") AS "días_txt", h.hora_inicial, h.hora_final,       CASE           WHEN h.hora_inicial IS
NULLTHEN ''::text           WHEN date_part('minute'::text, h.hora_inicial) = 0::double precision THEN
date_part('hour'::text,h.hora_inicial)::text           ELSE (date_part('hour'::text, h.hora_inicial)::text ||
':'::text)|| to_char(date_part('minute'::text, h.hora_inicial), 'fm00'::text)       END AS hora_inicial_txt,       CASE
         WHEN h.hora_final IS NULL THEN ''::text           WHEN date_part('minute'::text, h.hora_final) = 0::double
precisionTHEN date_part('hour'::text, h.hora_final)::text           ELSE (date_part('hour'::text, h.hora_final)::text
||':'::text) || to_char(date_part('minute'::text, h.hora_final), 'fm00'::text)       END AS hora_final_txt,
h."salón_id","salón_id_atxt"(h."salón_id") AS "salón_txt", h.nota AS horario_nota  FROM ordinario.horario h; 

The left join gives a secuential scan, no matter what:
explain analyze select * from regacd.profesor p left join vordinario.horario h on (h.profesor_id = p.id) where
p.grupo_idIN (129314, 129315, 129316, 129317, 129318, 129319, 129320, 129321, 129322);
                                                                                            QUERY PLAN

                                                           

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Merge
LeftJoin  (cost=2036.01..2107.72 rows=18 width=219) (actual time=1610.715..1611.626 rows=19 loops=1)  Merge Cond:
("outer".id= "inner".profesor_id)  ->  Sort  (cost=96.88..96.92 rows=18 width=25) (actual time=0.299..0.325 rows=19
loops=1)       Sort Key: p.id        ->  Index Scan using profesor_grupo, profesor_grupo, profesor_grupo,
profesor_grupo,profesor_grupo, profesor_grupo, profesor_grupo, profesor_grupo, profesor_grupo on profesor p
(cost=0.00..96.50rows=18 width=25) (actual time=0.062..0.220 rows=19 loops=1)              Index Cond: ((grupo_id =
129314)OR (grupo_id = 129315) OR (grupo_id = 129316) OR (grupo_id = 129317) OR (grupo_id = 129318) OR (grupo_id =
129319)OR (grupo_id = 129320) OR (grupo_id = 129321) OR (grupo_id = 129322))  ->  Sort  (cost=1939.13..1974.94
rows=14323width=194) (actual time=1581.038..1585.742 rows=13900 loops=1)        Sort Key: h.profesor_id        ->
SubqueryScan h  (cost=0.00..950.41 rows=14323 width=194) (actual time=1.180..1549.464 rows=14323 loops=1)
-> Seq Scan on horario h  (cost=0.00..936.09 rows=14323 width=45) (actual time=1.160..1450.191 rows=14323 loops=1)Total
runtime:1616.958 ms 
(11 filas)

Regards,
Manuel.


pgsql-sql by date:

Previous
From: Tom Lane
Date:
Subject: Re: Is it normal that functions are so much faster than inline queries
Next
From: Josh Berkus
Date:
Subject: Re: SQL Spec Compliance Questions