• 作者:老汪软件技巧
  • 发表时间:2024-09-04 04:02
  • 浏览量:

本文是《PostgreSQL技术问答》系列文章中的一篇。关于这个系列的由来,可以参阅开篇文章:

《PostgreSQL技术问答00 - Why Postgres》

文章的编号只是一个标识,在系列中没有明确的逻辑顺序和意义。读者进行阅读时,不用太关注这个方面。

本文讨论的内容是PostgreSQL提供的一种简化数据管理的方式: Inherites,表继承。

什么是表继承

Inherits,继承,是一个软件工程和面向对象编程(OOP)的一个核心和重要的概念。编程中的继承允许一个类(子类)继承另一个类(父类或基类)的属性和方法。通过继承,子类可以重用父类的代码,并且可以添加或覆盖父类的行为。继承的主要目的主要是:

在Postgres中,为了解决和优化一些在现实世界中的业务需求,模仿了对象编程中的继承概念,提出和实现了表继承的特性。在某些情况下,可以简化数据库的设计和操作。当然,虽然是模仿和借鉴了对象编程继承的概念,但在数据库实现中,由于主要针对表和数据结构的描述,它们还是有一些区别的(例如缺乏方法继承、重写等)。

下面我们通过一个例子,来了解这些细节问题。

举例说明

下面是一个表继承定义应用的例子:

-- 主表, 城市
CREATE TABLE city ( id serial PRIMARY KEY, name text );
-- 继承表, 首都
CREATE TABLE capital (  nation text ) INHERITS (city);
-- 插入主表数据
insert into city (name) values  ('上海'),('成都'),('芝加哥'), ('马赛');
-- 插入继承表数据
 insert into capital (nation,name) values  
 ('中国','北京'),('美国','华盛顿'),('法国','巴黎'), ('德国','柏林');
-- 查询主表数据
defaultdb=> select * from city;
 id |  name  
----+--------
  1 | 上海
  2 | 成都
  3 | 芝加哥
  4 | 马赛
  5 | 北京
  6 | 华盛顿
  7 | 巴黎
  8 | 柏林
(8 rows)
-- 查询继承表数据
defaultdb=> select * from capital;
 id |  name  | nation 
----+--------+--------
  5 | 北京   | 中国
  6 | 华盛顿 | 美国
  7 | 巴黎   | 法国
  8 | 柏林   | 德国
(4 rows)
-- 联合查询
defaultdb=> select C.*,P.nation from city C left join capital P on P.id = C.id;
 id |  name  | nation 
----+--------+--------
  1 | 上海   | 
  2 | 成都   | 
  3 | 芝加哥 | 
  4 | 马赛   | 
  5 | 北京   | 中国
  6 | 华盛顿 | 美国
  7 | 巴黎   | 法国
  8 | 柏林   | 德国
(8 rows)
-- 限制查询
defaultdb=> select * from only city ;
 id |  name  
----+--------
  1 | 上海
  2 | 成都
  3 | 芝加哥
  4 | 马赛
(4 rows)

在上面这个例子中,我们先用一个很常规的方式,定义了一个主表(city);然后基于主表,定义了一个继承表(capital);定义继承表的时候,需要指定其继承关系,并且只需要定义它特有的字段;然后基于数据的特点,决定是否插入到主表或者子表当中。这时数据集合的关系,就是子表中的数据,是主表数据的一个子集,但有扩展的属性。

根据上面的例子,我们可以总结出来一些表继承相关的特点:

能不能嵌套继承和多次继承

_简述继承view的流程_技术问答表

在前面的章节中,相信读者已经大致的理解了Postgres中的表继承的基本概念和实现方式。这里我们稍微扩展一下,来看看这个表继承是否支持一些更加灵活的应用方式,比如嵌套继承和多次继承。

嵌套继承,就是将继承表作为主表,再次进行继承。测试示例如下:

-- 创建继承表的继承表
defaultdb=> CREATE TABLE capital2 ( score int ) INHERITS (capital);
CREATE TABLE
-- 插入数据
defaultdb=> insert into capital2 ( name, nation, score) values ('东京','日本', 90);
INSERT 0 1
-- 查询数据
defaultdb=> select * from city ;
 id |  name  | population 
----+--------+------------
  1 | 上海   |          0
  2 | 成都   |          0
  3 | 芝加哥 |          0
  4 | 马赛   |          0
  5 | 北京   |          0
  6 | 华盛顿 |          0
  7 | 巴黎   |          0
  8 | 柏林   |          0
 13 | 东京   |          0
(9 rows)
defaultdb=> select * from capitals ;
ERROR:  relation "capitals" does not exist
LINE 1: select * from capitals ;
                      ^
defaultdb=> select * from capital ;
 id |  name  | nation | population 
----+--------+--------+------------
  5 | 北京   | 中国   |          0
  6 | 华盛顿 | 美国   |          0
  7 | 巴黎   | 法国   |          0
  8 | 柏林   | 德国   |          0
 13 | 东京   | 日本   |          0
(5 rows)
defaultdb=> select * from capital2 ;
 id | name | nation | population | score 
----+------+--------+------------+-------
 13 | 东京 | 日本   |          0 |    90
(1 row)

看起来继承嵌套是可以的。但显然,在进行实际操作中,要注意避免额外的复杂性和逻辑冲突。

多种继承,就是基于同一个主表,继承多个子表的情况。下面是实验代码:

defaultdb=> CREATE TABLE smallcity ( nation text, location text ) INHERITS (city);
CREATE TABLE
defaultdb=> insert into smallcity ( name, nation) values ('丽江','云南');
INSERT 0 1
defaultdb=> select * from city;
 id |  name  | population 
----+--------+------------
  1 | 上海   |          0
  2 | 成都   |          0
  3 | 芝加哥 |          0
  4 | 马赛   |          0
  5 | 北京   |          0
  6 | 华盛顿 |          0
  7 | 巴黎   |          0
  8 | 柏林   |          0
 14 | 丽江   |          0
 13 | 东京   |          0
(10 rows)
defaultdb=> select * from smallcity;
 id | name | population | nation | location 
----+------+------------+--------+----------
 14 | 丽江 |          0 | 云南   | 
(1 row)

看起来也是可以的。笔者感觉,Postgres中的表继承,在数据表之间的隔离是做的比较好的。父表和子表相对独立,只是在做实际操作的时候,再对数据进行逻辑的组合。

有什么使用场景和问题?

经过查阅技术材料,在使用表继承的时候,需要了解它有一些应用方面的限制:

Postgres提供的表继承的功能特性,可以在数据结构层面上实现不同数据集合之间的共性和特性关系,为一些数据结构和业务设计提供了一定的灵活性,如果设计使用得当,可以简化数据库设计、数据结构的维护和数据操作。它比较适合于描述包含关系的数据集之间的关系。

在很多种情况下,我们可以通过简单的属性表示,就可以将数据集在一个表中进行分类和标识,以实现类似继承和扩展的效果。当然,理论上而言,使用继承,可以减少数据对磁盘的占用(父表中不需要存储额外的数据字段)。

当然,对于有继承关系的数据库设计,本身会带来一些额外的设计和维护方面的复杂性,并且对于应用开发和数据操作,也提出了更高的要求。

笔者的理解,虽然继承这个特性的出发和想法很好,但考虑到关系数据库本身就有很强的数据关系描述的能力,就显得并不是特别必要,还会增加一些复杂性和逻辑干涉的风险,可能得不偿失。加之,现代化的应用和数据库设计风格,力求简单直接明了。 所以,好像在实际应用场景中,没有看到广泛的讨论和应用,也没有看到Postgres高调的展示和宣传这个特性。

小结

本文讨论了Postgres中,表继承(Inherits)的相关问题。继承的基本概念来自面向对象的编程,体现在数据库中,可以用于对表结构进行扩展,从而对多个拥有部分相同结构的数据集进行更好的管理和维护。