↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3
历史版本PostgreSQL 8.1 已于 2010 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

5.8. 继承 #

PostgreSQL实现了表继承,这对数据库设计者来说是一种有用的工具(SQL:1999及其后的版本定义了一种类型继承特性,但和这里介绍的继承有很大的不同)。

让我们从一个示例开始:假设我们要为城市建立一个数据模型。每个州有很多城市,但只有一个首府。我们希望能够快速检索任意特定州的首府城市。这可以通过创建两个表来实现:一个用于州首府,另一个用于非首府城市。然而,当我们想要查询某个城市的数据,而不关心它是不是首府时,会发生什么?继承特性将有助于解决这个问题。我们可以将capitals表定义为继承自cities表:

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

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

在这种情况下,capitals表继承了它的父表cities的所有列。州首府还有一个额外的列state用来表示它所属的州。

在PostgreSQL中,一个表可以从0个或者多个其他表继承,而对一个表的查询则可以引用一个表的所有行或者该表的所有行加上它所有的后代表。 默认情况是后一种行为。例如,下面的查询将查找所有高度高于500尺的城市的名称,包括州首府:

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

对于来自PostgreSQL教程(见第 2.1 节)的示例数据,它将返回:

   name    | altitude
-----------+-----------
 Las Vegas |      2174
 Mariposa  |      1953
 Madison   |       845

另一方面,下面的查询将找到所有高度超过 500 尺且不是州首府的城市:

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

   name    | altitude
-----------+-----------
 Las Vegas |      2174
 Mariposa  |      1953

这里的ONLY关键词指示查询只被应用于cities上,而其他在继承层次中位于cities之下的其他表都不会被该查询涉及。很多我们已经讨论过的命令(如SELECT、UPDATE和DELETE)都支持ONLY关键词。

在某些情况下,我们可能希望知道一个特定行来自于哪个表。每个表中的系统列tableoid可以告诉我们行来自于哪个表:

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

将会返回:

 tableoid |   name    | altitude
----------+-----------+-----------
   139793 | Las Vegas |      2174
   139793 | Mariposa  |      1953
   139798 | Madison   |       845

(如果重新生成这个结果,可能会得到不同的OID数字。)通过与pg_class进行连接可以看到实际的表名:

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

将会返回:

 relname  |   name    | altitude
----------+-----------+-----------
 cities   | Las Vegas |      2174
 cities   | Mariposa  |      1953
 capitals | Madison   |       845

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

INSERT INTO cities (name, population, altitude, state)
VALUES ('New York', NULL, NULL, 'NY');

我们也许会希望数据能以某种方式被路由到capitals表中,但这不会发生:INSERT总是向指定的表中插入。在某些情况下,可以通过使用一个规则(见第 34 章)将插入动作重定向。但是这对上面的情况并没有帮助,因为cities表根本就不包含state列,因而这个命令会在触发规则之前就被拒绝。

可以在继承层次中的表上定义检查约束。父表上的所有检查约束都将自动被它的所有子表继承。但其他类型的约束不会被继承。

一个表可以从超过一个的父表继承,在这种情况下它拥有父表们所定义的列的并集。任何定义在子表上的列也会被加入到其中。如果在这个集合中出现重名列,那么这些列将被“合并”,这样在子表中只会有一个这样的列。重名列能被合并的前提是这些列必须具有相同的数据类型,否则会导致错误。合并后的列将带有它所来自的任何一个列定义中的全部检查约束的拷贝。

表继承目前只能使用CREATE TABLE语句来定义。相关的CREATE TABLE AS语句不允许指定继承。目前没有办法通过添加继承链接把一个现有表变为子表。类似地,一旦定义了继承链接,除了完全删除该表之外,也没有办法从子表中移除继承链接。只要还有子表存在,父表就不能被删除。如果希望移除一个表和它的所有后代,一种简单的方法是使用CASCADE选项删除父表。

ALTER TABLE会把列的数据定义或检查约束上的任何变化沿着继承层次向下传播。同样,删除被其他表依赖的列只能使用CASCADE选项。对于同名列的合并与拒绝,ALTER TABLE遵循与CREATE TABLE相同的规则。

5.8.1. 注意事项 #

表访问权限不会被自动继承。因此,试图访问父表的用户要么必须对所有子表也拥有执行相同操作的权限,要么必须使用ONLY记法。向现有的继承层次中添加新的子表时,请注意为其授予所有需要的权限。

继承特性的一个严重限制是,索引(包括唯一约束)和外键约束只作用于单个表,而不作用于其继承子表。对于外键约束的引用端和被引用端,这一点都成立。因此,沿用上面的示例:

  • 如果我们把cities.name声明为UNIQUE或PRIMARY KEY,这并不能阻止capitals表中出现与cities中城市同名的行。而且这些重复行默认还会出现在针对cities的查询结果中。事实上,默认情况下capitals根本没有唯一约束,因此它可以包含多行同名记录。你当然可以给capitals添加唯一约束,但这仍无法阻止相对于cities的重复。

  • 相似地,如果我们指定cities.name REFERENCES某个其他表,该约束不会自动地传播到capitals。在此种情况下,我们可以变通地在capitals上手工创建一个相同的REFERENCES约束。

  • 如果让另一个表的某列REFERENCES cities(name),那么该表可以包含城市名称,但不能包含首府名称。对于这种情况,并没有什么好的变通办法。

这些不足未来可能会在某个发行版中修复,但在此期间,在决定继承是否适合你的应用时,仍需要非常小心。

已弃用

在PostgreSQL之前的版本中,默认行为是不在查询中包含子表。这种做法被发现容易出错,而且也违反了 SQL 标准。在旧语法中,要在查询中包含子表,需在表名后附加*。例如:

SELECT * from cities*;

你仍然可以通过附加*来显式指定扫描子表,也可以通过写ONLY来显式指定不扫描子表。但从 7.1 版开始,不带修饰的表名的默认行为是同时扫描其子表,而在此之前默认行为并非如此。要获得旧的默认行为,可以禁用sql_inheritance配置选项。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。