19

我正在将一堆数据加载到 PostgresQL 9.3 数据库中,然后我想刷新所有依赖于更新表的物化视图。有没有办法自动完成,而不是浏览每个视图并一个一个地刷新它们?我知道 Oracle 可以很容易地做到这一点,但在梳理 PostgreSQL 文档后我没有找到任何东西。

4

5 回答 5

28

看起来当前版本的 PostgreSQL (9.3.1) 没有这样的功能,不得不编写我自己的函数:

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;

(在 github 上:https ://github.com/sorokine/RefreshAllMaterializedViews )

于 2013-11-14T23:01:07.787 回答
8

如果物化视图不相互依赖,上述答案可以正常工作。如果不是这种情况,那么刷新物化视图的顺序很重要(即,您需要在刷新不依赖于任何其他物化视图的物化视图之前刷新那些物化视图)。下面的代码将生成物化视图的有序列表,以便它们可以以正确的顺序更新。

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
;

这可用于psql以适当的顺序刷新所有视图,如下所示:

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

或者,可以将最后一部分转换为在先前解决方案中所做的函数。

于 2018-07-17T13:21:52.963 回答
2

相同的方法,添加了在所有模式上运行它的选项,可选地同时运行。

CREATE OR REPLACE FUNCTION RefreshAllMaterializedViews(_schema TEXT DEFAULT '*', _concurrently BOOLEAN DEFAULT false)
RETURNS INT AS $$
  DECLARE
    r RECORD;
  BEGIN
    RAISE NOTICE 'Refreshing materialized view(s) in % %', CASE WHEN _schema = '*' THEN ' all schemas' ELSE 'schema "'|| _schema || '"' END, CASE WHEN _concurrently THEN 'concurrently' ELSE '' END;
    IF pg_is_in_recovery() THEN 
      RETURN 0;
    ELSE    
      FOR r IN SELECT schemaname, matviewname FROM pg_matviews WHERE schemaname = _schema OR _schema = '*' 
      LOOP
        RAISE NOTICE 'Refreshing %.%', r.schemaname, r.matviewname;
        EXECUTE 'REFRESH MATERIALIZED VIEW ' || CASE WHEN _concurrently THEN 'CONCURRENTLY ' ELSE '' END || '"' || r.schemaname || '"."' || r.matviewname || '"'; 
      END LOOP;
    END IF;
    RETURN 1;
  END 
$$ LANGUAGE plpgsql;

我也把它放在了 GitHub 上:https ://github.com/frankhommers/RefreshAllMaterializedViews

于 2015-12-04T10:02:53.937 回答
1

同样的方法,增加了备用检查

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;
    if pg_is_in_recovery()  then 
    return 1;
    else
    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;
    end if;
    RETURN 1;
END 
$$ LANGUAGE plpgsql;
于 2015-03-31T14:53:43.807 回答
1

下面的代码片段在该视图存在索引时REFRESH MATERIALIZED VIEW CONCURRENTLY使用。UNIQUE

CREATE OR REPLACE FUNCTION public.refresh_materialized_views()
  RETURNS void
AS
$BODY$
  DECLARE
      refresh_sql text;
    BEGIN

    WITH matviews AS (
       SELECT t.oid,
              relname AS view_name,
              nspname AS schema_name
         FROM pg_class t
         JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace
        WHERE t.relkind = 'm'
          AND nspname NOT LIKE 'pg-%'
    ), unique_indexes AS (
     SELECT m.oid,
            view_name,
            schema_name
       FROM pg_class i,
            pg_index ix,
            matviews m
      WHERE ix.indisunique = true
        AND ix.indexrelid = i.oid
        AND ix.indrelid = m.oid
    ), refresh_concurrently AS (
      SELECT 'REFRESH MATERIALIZED VIEW CONCURRENTLY ' || quote_ident(schema_name) || '.' || quote_ident(view_name) AS sql
        FROM unique_indexes
    ), refresh AS (
      SELECT 'REFRESH MATERIALIZED VIEW ' || quote_ident(schema_name) || '.' || quote_ident(view_name) AS sql
        FROM matviews
       WHERE oid != all (SELECT oid FROM unique_indexes)
    ), sql AS (
      SELECT sql FROM refresh_concurrently
      UNION ALL
      SELECT sql FROM refresh
    )

    SELECT string_agg(sql, E';\n') || E';\n' FROM sql INTO refresh_sql;

    EXECUTE refresh_sql;

    END;
$BODY$
LANGUAGE plpgsql VOLATILE;

此代码段接受模式名称以限制刷新的视图。

CREATE OR REPLACE FUNCTION public.refresh_materialized_views(_schema text)
  RETURNS void
AS
$BODY$
  DECLARE
      refresh_sql text;
    BEGIN

    WITH matviews AS (
       SELECT t.oid,
              relname AS view_name,
              nspname AS schema_name
         FROM pg_class t
         JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace
        WHERE t.relkind = 'm'
          AND nspname NOT LIKE 'pg-%'
          AND nspname = _schema
    ), unique_indexes AS (
     SELECT m.oid,
            view_name,
            schema_name
       FROM pg_class i,
            pg_index ix,
            matviews m
      WHERE ix.indisunique = true
        AND ix.indexrelid = i.oid
        AND ix.indrelid = m.oid
    ), refresh_concurrently AS (
      SELECT 'REFRESH MATERIALIZED VIEW CONCURRENTLY ' || quote_ident(schema_name) || '.' || quote_ident(view_name) AS sql
        FROM unique_indexes
    ), refresh AS (
      SELECT 'REFRESH MATERIALIZED VIEW ' || quote_ident(schema_name) || '.' || quote_ident(view_name) AS sql
        FROM matviews
       WHERE oid != all (SELECT oid FROM unique_indexes)
    ), sql AS (
      SELECT sql FROM refresh_concurrently
      UNION ALL
      SELECT sql FROM refresh
    )

    SELECT string_agg(sql, E';\n') || E';\n' FROM sql INTO refresh_sql;

    EXECUTE refresh_sql;

    END;
$BODY$
LANGUAGE plpgsql VOLATILE;
于 2016-03-14T14:45:36.680 回答