5.5. 继承

让我们创建两个表。首府表包含每个州的首府,它们也是城市。通常,首府表应该从城市表中继承过来。

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- (单位:英尺)
);

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

在这种情况下,一行首府从它的父表,城市表中继承所有属性(名字,人口以及海拔)。 州首府有一个额外的属性,state,显示它们所在的州。在 PostgreSQL 里, 一个表可以从零个或多个其它表中继承属性,而且一个查询既可以引用一个表中的所有行, 也可以引用一个表的所有行加上所有其后代表的行。

注意: 继承层次实际上是有向开环图。

比如,下面的查询查找所有海拔 500 英尺以上的所有城市的名字,包括州首府:

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

它返回:

   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

这里的 cities 前面的 "ONLY" 表面该查询应该只对 cities 进行查找而不包括继承级别低于 cities 的表。 许多我们已经讨论过的命令 -- SELECTUPDATEDELETE -- 支持这个 "ONLY" 符号。

废弃: 以前版本的 PostgreSQL 里,缺省是不访问子表。 我们发现这样是容易出错的而且违背 SQL:1999 标准。在旧语法里面,要访问子表,你需要附加一个 * 到表名后面。例如

SELECT * from cities*;

你仍然可以通过附加*明确声明需要扫描子表, 也可以通过写 "ONLY" 声明明确声明不扫描子表。 不过,从版本 7.1 开始,对那些不带修饰的表名子的缺省行为是同时扫描它的子表, 而以前的缺省是正相反。要获得老的缺省行为, 把配置选项 SQL_Inheritance 关闭,也就是∶

SET SQL_Inheritance TO OFF;

或者向你的 postgresql.conf 文件里面加一行。

有时候你可能想知道某条行版本来自哪个表。在每个表里我们都有一个系统属性叫 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

一个表可以从多于一个父表中继承,在这种情况下,它拥有它的父表们定义的字段的和 (加上任何为这个子表单独定义的字段)。

继承特性的一个严重的局限性是索引(包括唯一约束)和外键约束只施用于单个表, 而不包括它们的继承的子表。这一点不管对引用表还是被引用表都是事实,因此,在上面的例子里:

这些缺点很可能在将来的版本中修补,但同时你也需要考虑一下,继承是否对你的问题真正有用。