Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refresh all materialized views in Postgresql 9.3 at once?

I am loading a bunch of data into a PostgresQL 9.3 database and then I want to refresh all materialized views that depend on the updated tables. Is there a way to do it automatically instead of going through each view and refreshing them one by one? I know that Oracle can do that rather easily but I did not find anything after combing through PostgreSQL documentation.

like image 516
srk Avatar asked Nov 14 '13 15:11

srk


People also ask

How do you automatically refresh a materialized view?

You can also use the AUTO REFRESH clause to refresh materialized views automatically. For more information about creating materialized views, see CREATE MATERIALIZED VIEW. You can turn on autorefresh for a current materialized view by using ALTER MATERIALIZED VIEW.

How materialized view refresh works in PostgreSQL?

REFRESH MATERIALIZED VIEW completely replaces the contents of a materialized view. To execute this command you must be the owner of the materialized view. The old contents are discarded.

Why is my materialized view not refreshing automatically?

If you want the materialized view to be refreshed automatically you should use ON COMMIT refresh method. Since you have specified an ON DEMAND refresh you will have to manually refresh the materialized view using DBMS_MVIEW. REFRESH method. There are lot of considerations for refreshing a materialized view.

What is a materialized view refresh group?

A refresh group refreshes all materialized views in the group together. By creating a refresh group and including both the employee and salary materialized views, the data integrity between the materialized views is maintained. Refresh groups are created and maintained using the dbms_refresh package.


2 Answers

Looks like current version of PostgreSQL (9.3.1) does not have such functionality, have had to write my own function instead:

CREATE OR REPLACE FUNCTION RefreshAllMaterializedViews(schema_arg TEXT DEFAULT 'public')
RETURNS INT AS $$
DECLARE
    r RECORD;
BEGIN
    RAISE NOTICE 'Refreshing materialized view in schema %', schema_arg;
    FOR r IN SELECT matviewname FROM pg_matviews WHERE schemaname = schema_arg 
    LOOP
        RAISE NOTICE 'Refreshing %.%', schema_arg, r.matviewname;
        EXECUTE 'REFRESH MATERIALIZED VIEW ' || schema_arg || '.' || r.matviewname; 
    END LOOP;

    RETURN 1;
END 
$$ LANGUAGE plpgsql;

(on github: https://github.com/sorokine/RefreshAllMaterializedViews)

like image 66
srk Avatar answered Oct 09 '22 00:10

srk


The above answers work fine if the materialized views do not depend on each other. If that is not the case, then the order in which the materialized views are refreshed is important (i.e., you need to refresh the materialized views that don't depend on any other materialized views before you refresh those that do). The code below will generate an ordered list of materialized views so that they can be updated in the correct order.

CREATE OR REPLACE VIEW mat_view_dependencies AS
WITH RECURSIVE s(start_schemaname,start_mvname,schemaname,mvname,relkind,
               mvoid,depth) AS (
-- List of mat views -- with no dependencies
SELECT n.nspname AS start_schemaname, c.relname AS start_mvname,
n.nspname AS schemaname, c.relname AS mvname, c.relkind,
c.oid AS mvoid, 0 AS depth
FROM pg_class c JOIN pg_namespace n ON c.relnamespace=n.oid
WHERE c.relkind='m'
UNION
-- Recursively find all things depending on previous level
SELECT s.start_schemaname, s.start_mvname,
n.nspname AS schemaname, c.relname AS mvname,
c.relkind,
c.oid AS mvoid, depth+1 AS depth
FROM s
JOIN pg_depend d ON s.mvoid=d.refobjid
JOIN pg_rewrite r ON d.objid=r.oid
JOIN pg_class c ON r.ev_class=c.oid AND (c.relkind IN ('m','v'))
JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE s.mvoid <> c.oid -- exclude the current MV which always depends on itself
)
SELECT * FROM s;

CREATE OR REPLACE VIEW mat_view_refresh_order AS
WITH b AS (
-- Select the highest depth of each mat view name
SELECT DISTINCT ON (schemaname,mvname) schemaname, mvname, depth
FROM mat_view_dependencies
WHERE relkind='m'
ORDER BY schemaname, mvname, depth DESC
)
-- Reorder appropriately
SELECT schemaname, mvname, depth AS refresh_order
FROM b
ORDER BY depth, schemaname, mvname
;

This can be used in psql to refresh all views in the appropriate order as follows:

WITH a AS (
SELECT 'REFRESH MATERIALIZED VIEW "' || schemaname || '"."' || mvname || '";' AS r
FROM mat_view_refresh_order
ORDER BY refresh_order
)
SELECT string_agg(r,E'\n') AS script FROM a \gset

\echo :script
:script

This final part can, alternatively, be converted into a function as has been done in the previous solutions.

like image 43
user3004015 Avatar answered Oct 09 '22 01:10

user3004015