I am trying to build query that checks dead tuples, live tuples percent, free space etc. in all my users tables using pgstattuple extension. So, I came up with this query:
SELECT schemaname,
relname,
(pgstattuple(schemaname ||'.'||relname)).*
FROM pg_statio_user_tables;
pg_statio_user_tables is a pg_catalog view, that uses another system view. So at first glance the query looks quite simple, but it ended up with a total execution time of 27 minutes for 882 objects in db.
The execution plan for this query:
Is there a way to rewrite query still using pgstattuple so it'll perform decent?
It gets a lot faster if you run the function in a subquery and only decompose the resulting row type in the outer SELECT
list:
SELECT s.schemaname, s.relname, st.*
FROM pg_statio_user_tables s
CROSS JOIN LATERAL pgstattuple(s.relid) AS st
ORDER BY 1, 2; -- optional
This way, the function is only called once per row in pg_statio_user_tables
. You were stumbling over a weak spot in the Postgres query planner. See:
Since Postgres 9.4 pgstattuple()
can take an OID, which is preferable.