PostgreSQL-表继承

让我们从一个例子开始:假设我们正在尝试为城市构建一个数据模型。每个州都有许多城市,但只有一个首府。我们希望能够快速检索任何特定州的首都。这可以通过创建两张表来完成,一张用于州首府,一张用于非首都城市。但是,当我们想要查询一个城市的数据时会发生什么,无论它是否是首都?继承特性可以帮助解决这个问题。我们定义 capitals 表,以便它继承自cities:

CREATE TABLE cities (
    name            text,
    population      float,
    elevation       int     -- in feet
);

CREATE TABLE capitals (
    state           char(2)
) INHERITS (cities);

在这种情况下,capitals 表继承了其父表 citys 的所有列。州首府也有一个额外的列,州,显示他们的州。

在 PostgreSQL 中,一个表可以从零个或多个其他表继承,一个查询可以引用一个表的所有行或一个表的所有行加上它的所有后代表。后一种行为是默认行为。例如,以下查询查找海拔超过 500 英尺的所有城市的名称,包括州首府:

SELECT name, elevation
FROM cities
WHERE elevation > 500;

另一方面,以下查询查找所有非州首府且海拔超过 500 英尺的城市:

SELECT name, elevation
FROM ONLY cities
WHERE elevation > 500;

这里的 ONLY 关键字表示查询应该只应用于城市,而不是继承层次结构中城市下面的任何表。我们已经讨论过的许多命令——SELECT、UPDATE 和 DELETE——都支持 ONLY 关键字。

您还可以在表名后面加上 * 以明确指定包含后代表:

SELECT name, elevation
FROM cities*
WHERE elevation > 500;

不需要写 *,因为此行为始终是默认行为。但是,仍然支持此语法以与可以更改默认值的旧版本兼容。

在某些情况下,您可能希望知道特定行来自哪个表。每个表中都有一个名为 tableoid 的系统列,可以告诉您原始表:

SELECT c.tableoid, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;

通过使用 pg_class 进行连接,您可以看到实际的表名:

SELECT p.relname, c.name, c.elevation
FROM cities c, pg_class p
WHERE c.elevation > 500 AND c.tableoid = p.oid;

获得相同效果的另一种方法是使用 regclass 别名类型,它将象征性地打印表 OID:

SELECT c.tableoid::regclass, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;

继承不会自动将数据从 INSERT 或 COPY 命令传播到继承层次结构中的其他表。在我们的示例中,以下 INSERT 语句将失败:

INSERT INTO cities (name, population, elevation, state)
VALUES ('Albany', NULL, NULL, 'NY');

父表上的所有检查约束和非空约束都由其子表自动继承,除非使用 NO INHERIT 子句明确指定。不继承其他类型的约束(唯一、主键和外键约束)。

一个表可以从多个父表继承,在这种情况下,它具有父表定义的列的并集。子表定义中声明的任何列都会添加到这些列中。如果相同的列名出现在多个父表中,或者同时出现在父表和子表的定义中,则这些列被“合并”,这样子表中就只有一个这样的列。要合并,列必须具有相同的数据类型,否则会引发错误。可继承的检查约束和非空约束以类似的方式合并。因此,例如,如果合并列来自的任何一个列定义被标记为非空,则合并列将被标记为非空。检查约束如果名称相同则合并,如果条件不同则合并失败。

表继承通常在创建子表时使用 CREATE TABLE 语句的 INHERITS 子句建立。或者,已经以兼容方式定义的表可以使用 ALTER TABLE 的 INHERIT 变体添加新的父关系。为此,新的子表必须已经包含与父列具有相同名称和类型的列。它还必须包含与父项同名的检查约束和检查表达式。类似地,可以使用 ALTER TABLE 的 NO INHERIT 变体从子项中删除继承链接。当继承关系用于表分区时,像这样动态添加和删除继承链接可能很有用。

父表不能在其任何子表保留时被删除。如果从任何父表继承,子表的列或检查约束也不能被删除或更改。如果您希望删除一个表及其所有后代,一种简单的方法是使用 CASCADE 选项删除父表。

ALTER TABLE 将传播列数据定义中的任何更改并检查继承层次结构中的约束。同样,只有在使用 CASCADE 选项时才能删除其他表所依赖的列。ALTER TABLE 遵循与 CREATE TABLE 期间应用的重复列合并和拒绝相同的规则。

继承查询仅对父表执行访问权限检查。因此,例如,授予对城市表的 UPDATE 权限意味着当通过城市访问它们时,也允许更新 capitals 表中的行。这保留了数据(也)在父表中的外观。但是,如果没有额外的授权,就无法直接更新 capitals 表。以类似的方式,父表的行安全策略在继承查询期间应用于来自子表的行。子表的策略(如果有)仅在它是查询中显式命名的表时应用;在这种情况下,任何附加到其父级的策略都会被忽略。

外部表也可以是继承层次结构的一部分,可以作为父表或子表,就像常规表一样。如果外部表是继承层次结构的一部分,则外部表不支持的任何操作也不支持整个层次结构。

请注意,并非所有 SQL 命令都能够处理继承层次结构。用于数据查询、数据修改或模式修改的命令(例如,SELECT、UPDATE、DELETE、ALTER TABLE 的大多数变体,但不包括 INSERT 或 ALTER TABLE ... RENAME)通常默认包含子表并支持 ONLY符号以排除它们。执行数据库维护和调整的命令(例如 REINDEX、VACUUM)通常只适用于单个物理表,并且不支持对继承层次结构进行递归。

继承特性的一个严重限制是索引(包括唯一约束)和外键约束仅适用于单个表,而不适用于它们的继承子表。

posted @ 2022-09-03 15:08  shigp1  阅读(734)  评论(0编辑  收藏  举报