5.9. Inheritance
5.9.继承
PostgreSQL implements table inheritance, which can be a useful tool for database designers.
(SQL:1999 and later define a type inheritance feature, which differs in many respects from the features
described here.)
PostgreSQL实现了表的继承,这对于数据库设计者会是一个有用的工具。(SQL:1999及以后版本定义了一个继承类型特性,这与此处描述的继承不同。)
Let's start with an example: suppose we are trying to build a data model for cities. Each state has many
cities, but only one capital. We want to be able to quickly retrieve the capital city for any particular
state. This can be done by creating two tables, one for state capitals and one for cities that are not
capitals. However, what happens when we want to ask for data about a city, regardless of whether it is
a capital or not? The inheritance feature can help to resolve this problem. We define the
capitals
table so that it inherits from
cities
:
让我们从一个示例开始:假设我们要建立一个城市数据模型。每个州有许多城市,但只有一个首府。我们需要尽快查到任一指定州的首府。这可以通过创建两个表来实现,其中一个州首府表,一个没有首府的表。然而,如果我们想要查询一个城市的信息,而不管它是不是首府,这会怎么样呢?继承特性可以帮助解决此问题。我们创建继承自cities表的capitals表:
CREATE TABLE cities (
name text,
population float,
altitude int -- in feet
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);
In this case, the
capitals
table
inherits
all the columns of its parent table,
cities
. State capitals
also have an extra column,
state
, that shows their state.
上例中,capitals表继承其父表cities的所有列。其有一个额外的列state,以显示首府所在的州。
In PostgreSQL, a table can inherit from zero or more other tables, and a query can reference either
all rows of a table or all rows of a table plus all of its descendant tables. The latter behavior is the
default. For example, the following query finds the names of all cities, including state capitals, that
are located at an altitude over 500 feet:
PostgreSQL中,一个表可以继承自零到多个表,一个查询可以查询一个表的所有行或者该表及其所有继承表的所有行。后一种为默认行为。例如,以下查询检索所有海拔高于500英尺的城市,包含州首府:
SELECT name, altitude
FROM cities
WHERE altitude > 500;
Given the sample data from the PostgreSQL tutorial (see Section 2.1), this returns:
假设数据来自PostgreSQL基础概览( 第2.1节
),以上查询会返回:
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
On the other hand, the following query finds all the cities that are not state capitals and are situated
at an altitude over 500 feet:
另一方面,以下查询检索非州首府的所有海拔高于500英尺的城市:
SELECT name, altitude
FROM ONLY cities
WHERE altitude > 500;
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Here the
ONLY
keyword indicates that the query should apply only to
cities
, and not any tables
below
cities
in the inheritance hierarchy. Many of the commands that we have already discussed
—
SELECT
,
UPDATE
and
DELETE
— support the
ONLY
keyword.
此处的关键词ONLY意味着查询仅检索cities表中的数据,而不会检索其继承表。我们之前讨论的许多命令都支持ONLY关键词,例如SELECT,UPDATE和DELETE命令。
You can also write the table name with a trailing
*
to explicitly specify that descendant tables are
included:
也可以在表名后加星号以显式指定查询包含其继承表:
SELECT name, altitude
FROM cities*
WHERE altitude > 500;
Writing
*
is not necessary, since this behavior is always the default. However, this syntax is still
supported for compatibility with older releases where the default could be changed.
添加星号是非必须的,因为此行为为默认行为。然而,为了兼容旧的版本(旧版本中默认行为可以被修改),此语法仍然被支持。
In some cases you might wish to know which table a particular row originated from. There is a system
column called
tableoid
in each table which can tell you the originating table:
在有些场景下,你可能想要知道特定行具体属于哪个表。可以通过系统列tableoid以区分:
SELECT c.tableoid, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;
which returns:
返回:
tableoid | name | altitude
----------+-----------+----------
139793 | Las Vegas | 2174
139793 | Mariposa | 1953
139798 | Madison | 845
(If you try to reproduce this example, you will probably get different numeric OIDs.) By doing a join
with
pg_class
you can see the actual table names:
(实际执行中,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;
which returns:
返回:
relname | name | altitude
----------+-----------+----------
cities | Las Vegas | 2174
cities | Mariposa | 1953
capitals | Madison | 845
Another way to get the same effect is to use the
regclass
alias type, which will print the table OID
symbolically:
另一种实现的方法是使用regclasss别名类,这可以根据OID打印出表名:
SELECT c.tableoid::regclass, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;
Inheritance does not automatically propagate data from
INSERT
or
COPY
commands to other tables
in the inheritance hierarchy. In our example, the following
INSERT
statement will fail:
继承并不会自动的将insert或copy命令中的数据分发的继承关系中的其他表。示例中,以下的insert语句会执行失败:
INSERT INTO cities (name, population, altitude, state)
VALUES ('Albany', NULL, NULL, 'NY');
We might hope that the data would somehow be routed to the
capitals
table, but this does not
happen:
INSERT
always inserts into exactly the table specified. In some cases it is possible to redirect
the insertion using a rule (see Chapter 41). However that does not help for the above case because
the
cities
table does not contain the column
state
, and so the command will be rejected before
the rule can be applied.
我们可能希望数据可以被分发到capitals表中,但这不会发生:insert语句只会将数据插入到指定的表中。在一些场景下,可以使用规则来重定向插入操作(参考 第41章
)。
但是,这对上述情况却没有帮助,因为citys表不包含列state,因此在应用规则之前,该命令就已经被拒绝。
All check constraints and not-null constraints on a parent table are automatically inherited by its children,
unless explicitly specified otherwise with
NO INHERIT
clauses. Other types of constraints
(unique, primary key, and foreign key constraints) are not inherited.
如果没有显式的指定NO INHERIT子句,子表会自动继承父表中的检查约束和非空约束。其他类型的约束(唯一、主键和外键约束)则不会被继承。
A table can inherit from more than one parent table, in which case it has the union of the columns
defined by the parent tables. Any columns declared in the child table's definition are added to these.
If the same column name appears in multiple parent tables, or in both a parent table and the child's
definition, then these columns are “merged” so that there is only one such column in the child table. To
be merged, columns must have the same data types, else an error is raised. Inheritable check constraints
and not-null constraints are merged in a similar fashion. Thus, for example, a merged column will be
marked not-null if any one of the column definitions it came from is marked not-null. Check constraints
are merged if they have the same name, and the merge will fail if their conditions are different.
一个表可以继承自多个表,在这种情形下,它会有用所有父表列的集合。子表中定义的列会加入到其中。如果多父表中有相同列名,或者父表中的列名与子表定义列名相同,那么这些列名会结合到一起,这样子表中会只有一个列。当然,相同列名能够结合是有条件的,那就是需要有相同的数据类型,否则就会报错。检查约束和非空约束继承与此类似。因此,例如,合并前,只要有一列有非空约束,则合并之后的列即会有非空约束。如果检查约束名称相同,则会合并,但是如果检查条件不同,在合并时则会报错。
Table inheritance is typically established when the child table is created, using the
INHERITS
clause
of the CREATE TABLE statement. Alternatively, a table which is already defined in a compatible
way can have a new parent relationship added, using the
INHERIT
variant of ALTER TABLE. To do
this the new child table must already include columns with the same names and types as the columns
of the parent. It must also include check constraints with the same names and check expressions as
those of the parent. Similarly an inheritance link can be removed from a child using the
NO INHERIT
variant of
ALTER TABLE
. Dynamically adding and removing inheritance links like this can be useful
when the inheritance relationship is being used for table partitioning (see Section 5.10).
表继承在使用 CREATE TABLE
语句创建子表时,通过指定INHERITS子句建立。当然,对于已经存在的表,可以使用 ALTER TABLE
的INHERIT子句为其添加父表继承关系。要想使该操作成功,那么现有表必须与父表具有相同名的列名且具有相同的数据类型。子表中也必须具有与父表相同的检查约束名称及检查表达式。当然,也可以使用 ALTER TABLE
的NO INHERIT子句以删除继承关系。能够动态的添加和删除继承关系,对于表分区会特别有用(参见 第5.10节
)。
One convenient way to create a compatible table that will later be made a new child is to use the
LIKE
clause in
CREATE TABLE
. This creates a new table with the same columns as the source table.
If there are any
CHECK
constraints defined on the source table, the
INCLUDING CONSTRAINTS
option to
LIKE
should be specified, as the new child must have constraints matching the parent to
be considered compatible.
使用 CREATE TABLE中的LIKE子句,可以方便的创建稍后成为新子表的兼容表。该语句会创建一个与原表相同列的新表。如果原表中定义了检查约束,为了兼容起见,需要在LIKE子句中指定INCLUDING CONSTRAINTS选项,以使新子表与父表具有相同的约束。
A parent table cannot be dropped while any of its children remain. Neither can columns or check
constraints of child tables be dropped or altered if they are inherited from any parent tables. If you
wish to remove a table and all of its descendants, one easy way is to drop the parent table with the
CASCADE
option (see Section 5.13).
父表在删除所有子表之前不能被删除。子表中继承自父表的列及检查约束,不能被删除或更改。如果想要删掉某张表及其所有子表,最简单的方式是在删除表的时候指定CASCADE选项(参见 5.13节)。
ALTER TABLE will propagate any changes in column data definitions and check constraints down
the inheritance hierarchy. Again, dropping columns that are depended on by other tables is only possible
when using the
CASCADE
option.
ALTER TABLE
follows the same rules for duplicate column
merging and rejection that apply during
CREATE TABLE
.
ALTER TABLE
将传播列数据定义中的所有更改,并沿继承层次结构检查约束。 同样,仅当使用CASCADE选项时,才可以删除其他表所依赖的列。 对于在 CREATE TABLE
期间应用的重复列合并和拒绝, ALTER TABLE
遵循相同的规则。
Inherited queries perform access permission checks on the parent table only. Thus, for example, granting
UPDATE
permission on the
cities
table implies permission to update rows in the
capitals
table as well, when they are accessed through
cities
. This preserves the appearance that the data
is (also) in the parent table. But the
capitals
table could not be updated directly without an addi
tional grant. In a similar way, the parent table's row security policies (see Section 5.7) are applied to
rows coming from child tables during an inherited query. A child table's policies, if any, are applied
only when it is the table explicitly named in the query; and in that case, any policies attached to its
parent(s) are ignored.
继承查询仅检查父表上的访问权限。因此,例如,授予cities表的update权限意味着通过cities表访问的时候,也可以更新capitals表中的行。这控制父表中数据的访问。但是,如果没有进一步的授权,则不可以直接对表capitals进行修改。类似的,在继承查询中,父表的行级安全策略(参见 5.7节)会应用到来自子表的行。而对于子表中,如果存在策略,那么仅当该子表表名被显式引用的时候才有效,此时,会忽略父表的策略。
Foreign tables (see Section 5.11) can also be part of inheritance hierarchies, either as parent or child
tables, just as regular tables can be. If a foreign table is part of an inheritance hierarchy then any
operations not supported by the foreign table are not supported on the whole hierarchy either.
像普通表那样,
外表也可以作为父表或子表作为继承的一部分。如果外表作为继承层次的一部分,那么整个继承层次中,任何为外表所不允许的操作均不受支持。