5.4.1. Check Constraints
5.4.1.检查约束
A check constraint is the most generic constraint type. It allows you to specify that the value in a certain column must satisfy a Boolean (truth-value) expression. For instance, to require positive product prices, you could use:
检查约束是最通用的约束类型。你可以通过它定义某一列中的值必须符合布尔表达式(真值)。例如,你可以这样设置以仅接受正的产品价格:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
As you see, the constraint definition comes after the data type, just like default value definitions.Default values and constraints can be listed in any order. A check constraint consists of the key word CHECK followed by an expression in parentheses. The check constraint expression should involve the column thus constrained, otherwise the constraint would not make too much sense.
正如上所见,约束定义紧跟在数据类型之后,类似于默认值定义。默认值和约束的顺序可随意。检查约束由关键词CHECK以及用括号括起来的一个表达式组成。检查约束表达式中需要包含约束的列,否则的话,约束就没有任何意义了。
You can also give the constraint a separate name. 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. (If you don't specify a constraint name in this way, the system chooses a name for you.)
因此,使用关键字CONSTRAINT后加标识符及约束定义来定义一个命名的约束。(如果没有明确定义约束名称,则系统会自动生成名称。)
A check constraint can also refer to several columns. 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)
);
The first two constraints should look familiar. The third one uses a new syntax. It is not attached to a particular column, instead it appears as a separate item in the comma-separated column list. Column definitions and these constraint definitions can be listed in mixed order.
前两个约束看起来挺熟(刚开始讲过的语法)。第三个约束使用了个新的语法。它没有与特定的列绑定,而是在列的列表中以逗号分隔作为了单独的项目。列定义和这些约束定义的顺序可随意。
We say that the first two constraints are column constraints, whereas the third one is a table constraint because it is written separately from any one column definition. Column constraints can also be written as table constraints, while the reverse is not necessarily possible, since a column constraint is supposed to refer to only the column it is attached to. (PostgreSQL doesn't enforce that rule, but you should follow it if you want your table definitions to work with other database systems.) The above example could also be written as:
前两个约束称为列级约束,而第三个约束,因为它独立于列的定义而被称为表级约束。列级约束也可以写成表级约束的样子,但是反过来却不可行,因为列级约束仅应关联到它约束的列。(PostgreSQL并不强制遵守此规则,但是如果你想创建的表在其他数据库系统也可用,则最好遵守此规则。)上例也可写成:
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)
);
It's a matter of taste.
这只是个人习惯的问题。
Names can be assigned to table constraints in the same way as column constraints:
表级约束也可像列级约束那样赋予名字:
CREATE TABLE products (
product_no integer,
name text,
price numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price > discounted_price)
);
It should be noted that a check constraint is satisfied if the check expression evaluates to true or the null value. Since most expressions will evaluate to the null value if any operand is null, they will not prevent null values in the constrained columns. To ensure that a column does not contain null values, the not-null constraint described in the next section can be used.
需注意,如果检查表达式为真或为null值,则满足检查约束。因为大多数表达式在操作数为null时会求值为null,那么约束列中会可以存储null值。如果需要确保列中没有null值,则可以使用下节介绍的非空约束。
注:
PostgreSQL不支持引用非受检查的新(或更新)行的check约束。虽然违反此规定的CHECK约束在简单的场景下可用,但是却不能保证数据库一直满足此约束(特别是后续对检查行的修改)。这会导致数据库dump或reload失败。即使数据库中数据完全满足约束条件,也有可能会因为数据载入的顺序问题而导致无法满足约束而载入失败。如果可以,请使用UNIQUE、EXCLUDE或者FOREIGN KEY约束实现跨行或跨表的约束。
如果仅是想实现在数据插入时进行一次检查,那么可以考虑使用触发器实现。(此方式避免了dump和reload问题,因为pg_dump在载入数据之前都不会触发触发器。)
注:
PostgreSQL假定CHECK约束的条件是不变的,也即,对于同一输入行一定是相同结果的。此假设仅针对在出入或更新数据的CHECK检查。
但有个特例,就是CHECK约束限制中引用了用户自定义函数,而后面函数定义修改了。PostgreSQL不限制这种操作,但这种操作可能会导表中会有违反CHECK约束的行。同样会导致随后的dump或reload失败。推荐的解决方案是,首先使用ALTER TABLE删掉该CHECK约束,然后修订函数定义,重加约束,然后会重新检查表中所有数据。
mydb=# create table test_cons(id numeric,name varchar(100));
CREATE TABLE
mydb=# insert into test_cons values(1,'one');
INSERT 0 1
mydb=# alter table test_cons add constraint cons_chk check (id > 1);
ERROR: check constraint "cons_chk" of relation "test_cons" is violated by some row