PostgreSQL教程-数据定义-检查约束
最新学讯:近期OCP认证正在报名中,因考试人员较多请尽快报名获取最近考试时间,报名费用请联系在线老师,甲骨文官方认证,报名从速!
我要咨询5.4.1. 检查约束
一个检查约束是最普通的约束类型。它允许我们指定一个特定列中的值必须要满足一个布尔表达式。例如,为了要求正值的产品价格,我们可以使用:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
如你所见,约束定义就和默认值定义一样跟在数据类型之后。默认值和约束之间的顺序没有影响。一个检查约束有关键字CHECK以及其后的包围在圆括号中的表达式组成。检查约束表达式应该涉及到被约束的列,否则该约束也没什么实际意义。
我们也可以给与约束一个独立的名称。这会使得错误消息更为清晰,同时也允许我们在需要更改约束时能引用它。语法为:
CREATE TABLE products (
product_no integer,
name text,
price numeric CONSTRAINT positive_price CHECK (price > 0)
);
要指定一个命名的约束,请在约束名称标识符前使用关键词CONSTRAINT,然后把约束定义放在标识符之后(如果没有以这种方式指定一个约束名称,系统将会为我们选择一个)。
一个检查约束也可以引用多个列。例如我们存储一个普通价格和一个打折后的价格,而我们希望保证打折后的价格低于普通价格:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric CHECK (discounted_price > 0),
CHECK (price > discounted_price)
);
前两个约束看起来很相似。第三个则使用了一种新语法。它并没有依附在一个特定的列,而是作为一个独立的项出现在逗号分隔的列列表中。列定义和这种约束定义可以以混合的顺序出现在列表中。
我们将前两个约束称为列约束,而第三个约束为表约束,因为它独立于任何一个列定义。列约束也可以写成表约束,但反过来不行,因为一个列约束只能引用它所依附的那一个列(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)
);
甚至是:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0 AND price > discounted_price)
);
这只是口味的问题。
表约束也可以用列约束相同的方法来指定名称:
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)
);
需要注意的是,一个检查约束在其检查表达式值为真或空值时被满足。因为当任何操作数为空时大部分表达式将计算为空值,所以它们不会阻止被约束列中的空值。为了保证一个列不包含空值,可以使用下一节中的非空约束。
注意
PostgreSQL不支持引用表数据以外的要检查的新增或更新的行的CHECK约束。 虽然违反此规则的CHECK约束在简单测试中看起来能工作,它不能保证数据库不会达到约束条件为假(false)的状态(由于涉及的其他行随后发生了更改)。 这将导致数据库转储和重新加载失败。 即使完整的数据库状态与约束一致,重新加载也可能失败,因为行未按照满足约束的顺序加载。 如果可能的话,使用UNIQUE, EXCLUDE,或 FOREIGN KEY约束以表示跨行和跨表限制。
如果你希望的是在插入行时的时候对其他行进行一次性检查,而不是持续维护的一致性保证,一个自定义的 trigger 可以用于实现这个功能。 (此方法避免了转储/重新加载问题,因为pg_dump不会重新安装触发器直到重新加载数据之后,因此不会在转储/重新加载期间强制执行检查。)注意
PostgreSQL假定CHECK约束的条件是不可变的,也就是说,它们始终为同一输入行提供相同的结果。 这个假设是仅在插入或更新行时,而不是在其他时间检查CHECK约束的原因。 (上面关于不引用其他表数据的警告实际上是此限制的特殊情况。)
打破此假设的常见方法的一个示例是在 CHECK表达式中引用用户定义的函数,然后更改该函数的行为。 PostgreSQL不会禁止那样,但它不会注意到现在表中是否有行违反了CHECK约束。这将导致后续数据库转储和重新加载失败。 处理此类更改的建议方法是删除约束(使用ALTER TABLE),调整函数定义,然后重新添加约束,从而对所有表行进行重新检查。