具有相互依赖关系的运行SQL脚本

时间:2019-07-16 17:26:14

标签: sql postgresql

我有一个在后端使用postgres数据库的项目,并且我正在创建一个Dockerfile,该文件可以自动设置数据库的本地开发实例。该数据库包括大量功能,并且这些功能历来已存储在上下文相关的sql文件中,例如users.sql,companies.sql。这样做很好,因为只要进行更改,我就可以简单地执行REPLACE所有视图并删除/重新创建所有函数的相关sql文件。

但是,当尝试在新的postgres实例上运行这些脚本时(CREATE所有表之后),它们会失败,因为大多数视图/函数都引用了尚未定义的其他视图函数。

我已经开始调查是否可以按特定顺序运行脚本来避免此问题,但是由于并非出于此目的而设计脚本,因此可能是不可能的,并且存在大量实体玩,所以这很可能是一项艰巨的任务。

除了重组脚本之外,还有什么方法可以实现这一目标?

1 个答案:

答案 0 :(得分:2)

您可以编写一个简单的递归查询,该查询以不依赖于其他视图的所有视图开头,然后以递归方式添加依赖于这些视图的视图。然后以正确的顺序输出这些视图的视图定义,您已经获得了脚本:

WITH RECURSIVE viewids AS (
   /* all views that don't depend on other views */
   SELECT t.oid, 1 as level
   FROM pg_class t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
   WHERE r.rulename = '_RETURN'
     AND t.relkind = 'v'
     AND t.relnamespace NOT IN ('pg_catalog'::regnamespace,
                                'information_schema'::regnamespace,
                                'pg_toast'::regnamespace)
     AND NOT EXISTS (
            /* depends on a view */
            SELECT 1
            FROM pg_depend AS d
               JOIN pg_class AS t2 ON d.refobjid = t2.oid
            WHERE d.objid = r.oid
              AND d.classid = 'pg_rewrite'::regclass
              AND d.refclassid = 'pg_class'::regclass
              AND d.deptype = 'n'
              AND d.refobjsubid <> 0
              AND t2.relkind = 'v'
         )
     AND NOT EXISTS (
            /* depends on an extension */
            SELECT 1
            FROM pg_depend
            WHERE objid = t.oid
              AND classid = 'pg_class'::regclass
              AND refclassid = 'pg_extension'::regclass
              AND deptype = 'e'
         )
UNION ALL
   /* all views that depend on these views */
   SELECT t.oid, viewids.level + 1
   FROM pg_class AS t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
      JOIN pg_depend AS d ON d.objid = r.oid
      JOIN viewids ON viewids.oid = d.refobjid
   WHERE t.relkind = 'v'
     AND r.rulename = '_RETURN'
     AND d.classid = 'pg_rewrite'::regclass                            
     AND d.refclassid = 'pg_class'::regclass
     AND d.deptype = 'n'
     AND d.refobjsubid <> 0
)
/* order the views by level, eliminating duplicates */
SELECT format('CREATE VIEW %s AS%s',
              oid::regclass,
              pg_get_viewdef(oid::regclass))
FROM viewids
GROUP BY oid
ORDER BY max(level);