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

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 7.3 已于 2007 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

2.4. 约束 #

数据类型是一种限制能够存储在表中的数据类别的方法。但是对于很多应用来说,它们提供的约束太粗糙。例如,一个包含产品价格的列应该只接受正值。但是没有任何一种数据类型只接受正值。另一个问题是我们可能需要根据其他列或行来约束一个列中的数据。例如,在一个包含产品信息的表中,对于每个产品编号应该只有一行。

为此,SQL 允许我们在列和表上定义约束。约束让我们能够按照需要控制表中的数据。如果用户试图在列中存储违反约束的数据,就会报错。即使该值来自默认值定义,这条规则也同样适用。

2.4.1. 检查约束

检查约束是最通用的约束类型。它允许 你指定某个列中的值必须满足一个 任意表达式。例如,要求产品价格为正,可以 使用:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0)
);

如你所见,约束定义和默认值定义一样都写在数据类型之后。默认值和约束的先后顺序没有影响。检查约束由关键字CHECK以及其后放在圆括号中的表达式组成。检查约束表达式应当涉及被约束的列,否则这个约束就没有太大意义。

你还可以给约束一个单独的名字。 This clarifies error messages and allows you to refer to the constraint when you need to change it. The syntax is:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CONSTRAINT positive_price CHECK (price > 0)
);

So, to specify a named constraint, use the key word CONSTRAINT followed by an identifier followed by the constraint definition.

检查约束也可以引用多个列。 Say you store a regular price and a discounted price, and you want to ensure that the discounted price is lower than the regular price.

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);

前两个约束看起来很相似。第三个则使用了一种新语法。它并没有依附在一个特定的列,而是作为一个独立的项出现在逗号分隔的列列表中。列定义和这种约束定义可以以混合的顺序出现在列表中。

We say that the first two constraints are column constraints, whereas the third one is a table constraint because it is written separately from the column definitions. Column constraints can also be written as table constraints, while the reverse is not necessarily possible. The above example could also be written as

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);

or even

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0 AND price > discounted_price)
);

这只是口味问题。

应当注意,如果检查表达式的求值结果为真或空值, 检查约束就得到满足。由于大多数 表达式在有一个操作数为空时都会求值为空值, 它们不会阻止受约束列中的空值。要 确保列不包含空值,应当使用下一节描述的非空 约束。

2.4.2. 非空约束

非空约束只是指定某列不能取空值。语法示例如下:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric
);

非空约束总是写成列约束。从功能上看,非空约束等价于创建检查约束CHECK (column_name IS NOT NULL),但在PostgreSQL中创建显式的非空约束效率更高。缺点是无法为以那种方式创建的非空约束显式指定名称。

当然,一个列可以有多个约束。 Just write the constraints after one another:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric NOT NULL CHECK (price > 0)
);

The order doesn't matter. It does not necessarily affect in which order the constraints are checked.

NOT NULL 约束有一个反面:NULL 约束。这并不意味着列必须为空值——那肯定毫无用处。相反,它 只是定义了列可以为空值的默认行为。 NULL 约束没有在 SQL 标准中定义,不应在 可移植的应用中使用。(它被加入 PostgreSQL 只是为了与某些其他数据库系统兼容。)不过有些用户喜欢它, 因为它便于在脚本文件中切换约束。例如,你可以从

CREATE TABLE products (
    product_no integer NULL,
    name text NULL,
    price numeric NULL
);

and then insert the NOT key word where desired.

提示

在大多数数据库设计中,多数列都应标记为非空。

2.4.3. 唯一约束

唯一约束保证某一列或某一组列中保存的数据在整个表的所有行之间都是唯一的。写成列约束时的语法是:

CREATE TABLE products (
    product_no integer UNIQUE,
    name text,
    price numeric
);

写成表约束时则是:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    UNIQUE (product_no)
);

如果唯一约束引用一组列,这些列 are listed separated by commas:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    UNIQUE (a, c)
);

也可以给唯一约束指定名称:

CREATE TABLE products (
    product_no integer CONSTRAINT must_be_different UNIQUE,
    name text,
    price numeric
);

一般而言,当表中(至少)有两行在作为约束 一部分的各对应列上的值都相等时,唯一约束 就被违反。但在这种 判断中空值不被视为相等。这意味着在存在多列 唯一约束时,也可以存储不限数量的 至少一个受约束列包含空值的行。这种 行为符合 SQL 标准,但我们 听说其他 SQL 数据库可能不遵循这条规则。所以在开发 打算移植的应用时要 小心。

2.4.4. 主键

从技术上讲,主键约束只是唯一约束和非空约束的组合。因此,下面的两个表定义接受相同的数据:

CREATE TABLE products (
    product_no integer UNIQUE NOT NULL,
    name text,
    price numeric
);
CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

主键也可以包含多于一个列,其语法和唯一约束相似:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    PRIMARY KEY (a, c)
);

主键表示一个列或一组列可以 用作表中行的唯一标识符。(这是 主键定义的直接结果。注意 唯一约束实际上并不提供唯一标识符, 因为它不排除空值。)这对于 文档目的和客户端应用都很有用。例如, 允许修改行值的 GUI 应用可能需要知道表的主键才能唯一地 标识行。

一个表最多只能有一个主键(但它可以有多个唯一和非空约束)。关系数据库理论要求每一个表都要有一个主键。PostgreSQL 并未强制要求这一点,但通常最好遵循它。

2.4.5. 外键 #

一个外键约束指定一列(或一组列)中的值必须匹配出现在另一个表中某些行的值。我们说这维持了两个关联表之间的引用完整性。

假设你有我们已经用过多次的产品表:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

Let's also assume you have a table storing orders of those products. We want to ensure that the orders table only contains orders of products that actually exist. So we define a foreign key constraint in the orders table that references the products table:

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products (product_no),
    quantity integer
);

Now it is impossible to create orders with product_no entries that do not appear in the products table.

我们说在这种情况下,订单表是引用表而产品表是被引用表。相应地,也有引用和被引用列的说法。

你也可以把上面的命令简写成

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products,
    quantity integer
);

because in absence of a column list the primary key of the referenced table is used as referenced column.

外键也可以约束和引用一组列。 As usual, it then needs to be written in table constraint form. Here is a contrived syntax example:

CREATE TABLE t1 (
  a integer PRIMARY KEY,
  b integer,
  c integer,
  FOREIGN KEY (b, c) REFERENCES other_table (c1, c2)
);

Of course, the number and type of the constrained columns needs to match the number and type of the referenced columns.

一个表可以包含多个外键约束。 This is used to implement many-to-many relationships between tables. Say you have tables about products and orders, but now you want to allow one order to contain possibly many products (which the structure above did not allow). You could use this table structure:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products,
    order_id integer REFERENCES orders,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);

Note also that the primary key overlaps with the foreign keys in the last table.

我们知道外键禁止创建与任何产品无关的订单。但如果在创建引用某个产品的订单之后该产品被删除了会怎样?SQL 也允许你指定这种情况。直观上,我们有几种选择:

  • 不允许删除被引用的产品

  • 同时删除订单

  • 其他处理方式?

为了说明这一点,让我们在上面的多对多关系示例上 实现下面的策略:当有人想要删除一个仍被订单(经由 order_items)引用的产品时,我们不允许。如果有人 删除一个订单,订单项也随之删除。

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products ON DELETE RESTRICT,
    order_id integer REFERENCES orders ON DELETE CASCADE,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);

限制和级联删除是两个最常见的选项。 RESTRICT 也可以写成 NO ACTION,而且在你什么都没有指定时它也是 默认行为。对于主键被删除时外键列 应当发生什么,还有另外两个选项: SET NULL 和 SET DEFAULT。 注意这些并不能免除你遵守约束的义务。 例如,如果某个动作指定了 SET DEFAULT 而默认值不满足外键,主键的删除 将失败。

与 ON DELETE 类似,还有 ON UPDATE,它在主键被更改(更新)时 被调用。可能的动作是相同的。

更多关于更新和删除数据的信息请见第 3 章。

最后,我们要提到外键必须引用 是主键或构成唯一约束的列。 如果外键引用唯一约束,关于空值如何匹配有 一些额外的可能性。 这些在 PostgreSQL 7.3.21 参考手册 的 CREATE TABLE 条目中有说明。

提交更正

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