PostgreSQL 递归SQL 找出对象依赖

简介: 在使用数据库时,如果用到了视图,物化视图。在表,视图,物化视图这些对象之间就会产生依赖。例如 create table t(id int); create view v1 as select * from t; create view v2 as select * from v1; cr

在使用数据库时,如果用到了视图,物化视图。
在表,视图,物化视图这些对象之间就会产生依赖。
例如

create table t(id int);
create view v1 as select * from t;
create view v2 as select * from v1;
create view v3 as select v1.id from v1,v2 where v1.id=v2.id;
create view v4 as   SELECT v1.id +
    FROM v1,   +
     v2,       +
     pg_class, +
     pg_authid;
。。。。


依赖关系导致的报错

如果要改t的字段,或者删除t表。 会怎样呢?

postgres=# drop table t;
ERROR:  2BP01: cannot drop table t because other objects depend on it
DETAIL:  view v1 depends on table t
view v2 depends on view v1
view v3 depends on view v1
view v4 depends on view v1
materialized view v5 depends on view v4
materialized view v6 depends on view v4
view vv1v depends on table t
view vv1v1 depends on view vv1v
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
LOCATION:  reportDependentObjects, dependency.c:986

使用drop table t cascade可以自动删除依赖对象。

如果是改字段,对不起,需要把依赖对象先删掉,并重建依赖对象。

postgres=# alter table t alter column id type int8;

ERROR:  0A000: cannot alter type of a column used by a view or rule
DETAIL:  rule _RETURN on view vv1v depends on column "id"
LOCATION:  ATExecAlterColumnType, tablecmds.c:8225

DROP的时候,会通过reportDependentObjects函数打印依赖t表的视图。
代码如下

 src/backend/catalog/objectaddress.c

/*
 * reportDependentObjects - report about dependencies, and fail if RESTRICT
 *
 * Tell the user about dependent objects that we are going to delete
 * (or would need to delete, but are prevented by RESTRICT mode);
 * then error out if there are any and it's not CASCADE mode.
 *
 *    targetObjects: list of objects that are scheduled to be deleted
 *    behavior: RESTRICT or CASCADE
 *    msglevel: elog level for non-error report messages
 *    origObject: base object of deletion, or NULL if not available
 *        (the latter case occurs in DROP OWNED)
 */
static void
reportDependentObjects(const ObjectAddresses *targetObjects,
                       DropBehavior behavior,
                       int msglevel,
                       const ObjectAddress *origObject)
{
...


依赖信息在哪里

但是我们不DROP怎么知道依赖关系呢?
视图和物化视图其实都在pg_rewrite中,通过查询ev_action就可以得到定义。

postgres=# select ev_action from pg_rewrite where ev_class='v1'::regclass;
 ({QUERY :commandType 1 :querySource 0 :canSetTag true :utilityStmt <> :resultRelation 0 :hasAggs false :hasWindowFuncs false :hasSubLinks false :hasDistinctOn false :hasRecursive false :hasModifyingCTE false :hasForUpdate false :hasRowS
ecurity false :cteList <> :rtable ({RTE :alias {ALIAS :aliasname old :colnames <>} :eref {ALIAS :aliasname old :colnames ("id")} :rtekind 0 :relid 13090504 :relkind v :tablesample <> :lateral false :inh false :inFromCl false :requiredPer
ms 0 :checkAsUser 0 :selectedCols (b) :insertedCols (b) :updatedCols (b) :securityQuals <>} {RTE :alias {ALIAS :aliasname new :colnames <>} :eref {ALIAS :aliasname new :colnames ("id")} :rtekind 0 :relid 13090504 :relkind v :tablesample 
<> :lateral false :inh false :inFromCl false :requiredPerms 0 :checkAsUser 0 :selectedCols (b) :insertedCols (b) :updatedCols (b) :securityQuals <>} {RTE :alias <> :eref {ALIAS :aliasname t :colnames ("id")} :rtekind 0 :relid 13090484 :r
elkind r :tablesample <> :lateral false :inh true :inFromCl true :requiredPerms 2 :checkAsUser 0 :selectedCols (b 9) :insertedCols (b) :updatedCols (b) :securityQuals <>}) :jointree {FROMEXPR :fromlist ({RANGETBLREF :rtindex 3}) :quals <
>} :targetList ({TARGETENTRY :expr {VAR :varno 3 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 3 :varoattno 1 :location 25} :resno 1 :resname id :ressortgroupref 0 :resorigtbl 13090484 :resorigcol 1 :resjunk
 false}) :onConflict <> :returningList <> :groupClause <> :groupingSets <> :havingQual <> :windowClause <> :distinctClause <> :sortClause <> :limitOffset <> :limitCount <> :rowMarks <> :setOperations <> :constraintDeps <>})
(1 row)


如何递归的找出依赖

通过解析这个规则,可以得到依赖的对象OID。
创建一个解析函数,得到依赖的OID

create or replace function get_dep_oids(oid) returns oid[] as 
$$

declare
  res oid[];
begin
  select array_agg(unnest::oid) into res from 
  (
    select unnest(regexp_matches(ev_action::text,':relid (\d+)', 'g')) from pg_rewrite where ev_class = $1 
  union 
    select unnest(regexp_matches(ev_action::text,':resorigtbl (\d+)','g')) from pg_rewrite where ev_class = $1 
  EXCEPT 
    select oid::text from pg_class where oid=$1 
  ) t;
return res;
end;

$$
 language plpgsql strict;

例子
查询V1依赖的对象OID

postgres=# select * from get_dep_oids('v1'::regclass);
 get_dep_oids 
--------------
 {13090484}
(1 row)

再创建一个函数,递归的得到依赖的对象。

create or replace function recursive_get_deps(IN tbl oid, OUT oid oid, OUT relkind "char", OUT nspname name, OUT relname name, OUT deps oid[], OUT ori_oid oid, OUT ori_relkind "char", OUT ori_nspname name, OUT ori_relname name ) returns setof record as

$$

declare
begin
return query 
with recursive a as (
  select * from (
    select t1.oid,t1.relkind,t2.nspname,t1.relname,get_dep_oids(t1.oid) deps,(select t1.oid from pg_class t1,pg_namespace t2 where t1.relnamespace=t2.oid and t1.oid=tbl) as ori_oid from pg_class t1, pg_namespace t2 where t1.relnamespace=t2.oid and t1.relkind in ('m','v')
  ) t where t.ori_oid = any(t.deps)
union 
  select * from (
    select t1.oid,t1.relkind,t2.nspname,t1.relname,get_dep_oids(t1.oid) deps, a.oid as ori_oid from pg_class t1,pg_namespace t2,a where t1.relnamespace=t2.oid and t1.relkind in ('m','v')
  ) t where t.ori_oid = any(t.deps)
)
select a.oid,a.relkind,a.nspname,a.relname,a.deps,a.ori_oid,b.relkind ori_relkind, c.nspname ori_nspname,b.relname ori_relname from a,pg_class b,pg_namespace c where a.ori_oid=b.oid and b.relnamespace=c.oid order by a.nspname,a.relkind,a.relname;
end;

$$
 language plpgsql strict;

例子 :
查询所有直接或间接依赖t表的对象

postgres=# select * from recursive_get_deps('t'::regclass);
   oid    | relkind | nspname | relname |                  deps                   | ori_oid  | ori_relkind | ori_nspname | ori_relname 
----------+---------+---------+---------+-----------------------------------------+----------+-------------+-------------+-------------
 13090804 | m       | public  | v5      | {13090794}                              | 13090794 | v           | public      | v4
 13090808 | m       | public  | v6      | {13090804,13090794,0}                   | 13090804 | m           | public      | v5
 13090808 | m       | public  | v6      | {13090804,13090794,0}                   | 13090794 | v           | public      | v4
 13090504 | v       | public  | v1      | {13090484}                              | 13090484 | r           | public      | t
 13090508 | v       | public  | v2      | {13090504}                              | 13090504 | v           | public      | v1
 13090790 | v       | public  | v3      | {13090508,13090504}                     | 13090504 | v           | public      | v1
 13090790 | v       | public  | v3      | {13090508,13090504}                     | 13090508 | v           | public      | v2
 13090794 | v       | public  | v4      | {13090508,1259,1260,13090504}           | 13090504 | v           | public      | v1
 13090794 | v       | public  | v4      | {13090508,1259,1260,13090504}           | 13090508 | v           | public      | v2
 13090815 | v       | public  | vv1v    | {13090484}                              | 13090484 | r           | public      | t
 13090819 | v       | public  | vv1v1   | {13090508,13090790,13090504,13090815,0} | 13090790 | v           | public      | v3
 13090819 | v       | public  | vv1v1   | {13090508,13090790,13090504,13090815,0} | 13090508 | v           | public      | v2
 13090819 | v       | public  | vv1v1   | {13090508,13090790,13090504,13090815,0} | 13090815 | v           | public      | vv1v
 13090819 | v       | public  | vv1v1   | {13090508,13090790,13090504,13090815,0} | 13090504 | v           | public      | v1
(14 rows)

查到直接和间接依赖t表的对象有v5,v6,v1,v2,v3,v4,vv1v,vv1v1, 和之前DROP table t的报错内容一致。
是不是很帅呢

获取视图定义

拿到依赖关系后,我们还可以通过pg_get_viewdef拿到视图的定义

postgres=# select * from pg_get_viewdef('v4',false);
 pg_get_viewdef 
----------------
  SELECT v1.id +
    FROM v1,   +
     v2,       +
     pg_class, +
     pg_authid;
(1 row)

递归语法请参考我之前写的文章
https://yq.aliyun.com/articles/54657

小结

主要用到的技巧
.1. 规则表达式匹配 regexp_matches
https://www.postgresql.org/docs/9.6/static/functions-matching.html
.2. 递归查询 with recursive query
https://yq.aliyun.com/articles/54657

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
808 152
|
3月前
|
关系型数据库 分布式数据库 数据库
阿里云数据库收费价格:MySQL、PostgreSQL、SQL Server和MariaDB引擎费用整理
阿里云数据库提供多种类型,包括关系型与NoSQL,主流如PolarDB、RDS MySQL/PostgreSQL、Redis等。价格低至21元/月起,支持按需付费与优惠套餐,适用于各类应用场景。
|
6月前
|
SQL 关系型数据库 MySQL
Go语言数据库编程:使用 `database/sql` 与 MySQL/PostgreSQL
Go语言通过`database/sql`标准库提供统一数据库操作接口,支持MySQL、PostgreSQL等多种数据库。本文介绍了驱动安装、连接数据库、基本增删改查操作、预处理语句、事务处理及错误管理等内容,涵盖实际开发中常用的技巧与注意事项,适合快速掌握Go语言数据库编程基础。
507 62
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
|
6月前
|
SQL 关系型数据库 PostgreSQL
CTE vs 子查询:深入拆解PostgreSQL复杂SQL的隐藏性能差异
本文深入探讨了PostgreSQL中CTE(公共表表达式)与子查询的选择对SQL性能的影响。通过分析两者底层机制,揭示CTE的物化特性及子查询的优化融合优势,并结合多场景案例对比执行效率。最终给出决策指南,帮助开发者根据数据量、引用次数和复杂度选择最优方案,同时提供高级优化技巧和版本演进建议,助力SQL性能调优。
637 1
|
10月前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
337 2
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
SQL 存储 关系型数据库
PostgreSQL核心之SQL基础学习
PostgreSQL核心之SQL基础学习
386 3
|
SQL 关系型数据库 MySQL
INSERT INTO t_a.tableName SELECT * FROM t_b.tableName 如何通过定义一个list对象,包含多个tableName,循环执行前面的sql,用MySQL的语法写
【8月更文挑战第7天】INSERT INTO t_a.tableName SELECT * FROM t_b.tableName 如何通过定义一个list对象,包含多个tableName,循环执行前面的sql,用MySQL的语法写
186 5

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多