Reputation: 43
I got a Postgres database with multiple schemas. I'm trying to optimise my database tables with optimal data types. more often I end with the error
cannot alter the type of a column used by a view
while using the query alter table schema.tbl_name alter column column_name type varchar(5) using column_name::varchar(5);
Is there any way (function) that I could list all the views related to the table?
Upvotes: 4
Views: 3860
Reputation: 27012
This is doable, but it's tricky for 3 reasons:
But wonderfully there is a fairly efficient way that just queries one table, pg_depend, by leveraging the slightly magical regclass Object identifier type, and by using a recursive CTE
-- A view is a row in pg_rewrite, and a corresponding set of rows in pg_depend where:
--
-- 1. There is a row per column that links pg_rewrite with all the columns of all the tables (or
-- other views) in pg_class that the view queries
-- 2. There is also one "internal" row linking the pg_rewrite row with its pg_class entry for the
-- view itself. This is can be used to then find the views that query the view, the fully
-- qualified name of the view, as well as its definition.
--
-- So to find all the direct views for a table, we find the rows in pg_depend for 1., and then self
-- join onto pg_depend again for 2. (making sure to not choose the same rows again). To then find
-- all the indirect views, we do the same thing repeatedly using a recursive CTE.
--
-- PostgreSQL does not forbid cycles of views, so we have to make sure we never add a view that has
-- already been added.
-- Non recursive term: direct views on the table
WITH RECURSIVE views_on_table AS (
SELECT
view_depend.refobjid, ARRAY[view_depend.refobjid] AS path
FROM
pg_depend AS view_depend, pg_depend AS table_depend
WHERE
-- Find the rows in pg_rewrite for 1: rows that link the table with the views that query it
table_depend.classid = 'pg_rewrite'::regclass
AND table_depend.refclassid = 'pg_class'::regclass
AND table_depend.refobjid = 'schema_name.table_name'::regclass
-- And we can find 2: the pg_class entry for each of the views themselves
AND view_depend.classid = 'pg_rewrite'::regclass
AND view_depend.refclassid = 'pg_class'::regclass
AND view_depend.deptype = 'i'
AND view_depend.objid = table_depend.objid
AND view_depend.refobjid != table_depend.refobjid
UNION
-- Recursive term: views on the views
SELECT
view_depend.refobjid, views_on_table.path || ARRAY[view_depend.refobjid]
FROM
views_on_table, pg_depend AS view_depend, pg_depend AS table_depend
WHERE
-- Find the rows in pg_rewrite for 1: rows that link the views with other views that query it
table_depend.classid = 'pg_rewrite'::regclass
AND table_depend.refclassid = 'pg_class'::regclass
AND table_depend.refobjid = views_on_table.refobjid
-- And we can find 2: the pg_class entry for each of the views themselves
AND view_depend.classid = 'pg_rewrite'::regclass
AND view_depend.refclassid = 'pg_class'::regclass
AND view_depend.deptype = 'i'
AND view_depend.objid = table_depend.objid
AND view_depend.refobjid != table_depend.refobjid
-- Making sure to not get into an infinite cycle
AND NOT view_depend.refobjid = ANY(views_on_table.path)
)
SELECT views_on_table.refobjid::regclass::text AS fully_qualified_name
FROM views_on_table
This was inspired by the definition of information_schema.view_table_usage, and the discussion at https://github.com/uktrade/pg-bulk-ingest/pull/235
Upvotes: 0
Reputation: 425
Based on Bohemian's answer, here's a view and subsequent query that will generate all views for a certain table.
It won't catch views that depend on views which depend on your table, though.
CREATE OR REPLACE VIEW dba.v_views_on_table AS
select vtu.view_schema
, vtu.view_name
, vtu.table_schema
, vtu.table_name
, v.view_definition
from information_schema.view_table_usage vtu
join information_schema.views v on (vtu.view_schema = v.table_schema
and vtu.view_name = v.table_name)
where vtu.table_schema not in ('information_schema', 'pg_catalog')
order by vtu.view_schema, vtu.view_name
;
\pset format unaligned
\pset tuples_only
\o sometable_views.sql
select format('CREATE VIEW %s AS %s %s'
, u.view_schema||'.'||u.view_name
, E'\n'
, v.view_definition)
from information_schema.view_table_usage u
join information_schema.views v on u.view_schema = v.table_schema
and u.view_name = v.table_name
where u.table_schema||'.'||u.table_name = 'someschema.sometable'
order by u.view_schema, u.view_name
;
Upvotes: 0
Reputation: 425033
Use this query:
select
u.view_schema schema_name,
u.view_name,
u.table_schema referenced_table_schema,
u.table_name referenced_table_name,
v.view_definition
from information_schema.view_table_usage u
join information_schema.views v on u.view_schema = v.table_schema
and u.view_name = v.table_name
where u.table_schema not in ('information_schema', 'pg_catalog')
order by u.view_schema, u.view_name
Credit: Dataedo.com's article List tables used by a view in PostgreSQL database
Upvotes: 8