尝试移除一个不存在的表会引起错误。然而,在SQL脚本中在创建每个表之前无条件地尝试移除它的做法是很常见的,即使发生错误也会忽略之,因此这样的脚本可以在表存在和不存在时都工作得很好(如果你喜欢,可以使用DROP TABLE IF EXISTS
变体来防止出现错误消息,但这并非标准SQL)。
5.3.1. 检查约束
一个检查约束是最普通的约束类型。它允许我们指定一个特定列中的值必须要满足一个布尔表达式。例如,为了要求正值的产品价格,我们可以使用:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
5.3.2. 非空约束
一个非空约束总是被写成一个列约束。一个非空约束等价于创建一个检查约束CHECK (
,但在PostgreSQL中创建一个显式的非空约束更高效。这种方式创建的非空约束的缺点是我们无法为它给予一个显式的名称。column_name
IS NOT NULL)
NOT NULL
约束有一个相反的情况:NULL
约束。这并不意味着该列必须为空,进而肯定是无用的。相反,它仅仅选择了列可能为空的默认行为。SQL标准中并不存在NULL
约束,因此它不能被用于可移植的应用中(PostgreSQL中加入它是为了和某些其他数据库系统兼容)。但是某些用户喜欢它,因为它使得在一个脚本文件中可以很容易的进行约束切换。例如,初始时我们可以:
CREATE TABLE products ( product_no integer NULL, name text NULL, price numeric NULL );
然后可以在需要的地方插入NOT
关键词。
5.3.3. 唯一约束
唯一约束保证\在一列中或者一组列中保存的数据在表中所有行间是唯一的。
通常,如果表中有超过一行在约束所包括列上的值相同,将会违反唯一约束。但是在这种比较中,两个空值被认为是不同的。这意味着即便存在一个唯一约束,也可以存储多个在至少一个被约束列中包含空值的行。这种行为符合SQL标准,但我们听说一些其他SQL数据库可能不遵循这个规则。所以在开发需要可移植的应用时应注意这一点。
5.3.4. 主键
一个主键约束表示可以用作表中行的唯一标识符的一个列或者一组列。这要求那些值都是唯一的并且非空。
一个表最多只能有一个主键(可以有任意数量的唯一和非空约束,它们可以达到和主键几乎一样的功能,但只能有一个被标识为主键)。关系数据库理论要求每一个表都要有一个主键。但PostgreSQL中并未强制要求这一点,但是最好能够遵循它。
5.3.5. 外键
一个外键约束指定一列(或一组列)中的值必须匹配出现在另一个表中某些行的值。我们说这维持了两个关联表之间的引用完整性。
我们知道外键不允许创建与任何产品都不相关的订单。但如果一个产品在一个引用它的订单创建之后被移除会发生什么?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
表示在约束被检察时如果有任何引用行存在,则会抛出一个错误,这是我们没有指定任何东西时的默认行为(这两种选择的本质不同在于NO ACTION
允许检查被推迟到事务的最后,而RESTRICT
则不会)。
CASCADE
指定当一个被引用行被删除后,引用它的行也应该被自动删除。
还有其他两种选项:SET NULL
和SET DEFAULT
。这些将导致在被引用行被删除后,引用行中的引用列被置为空值或它们的默认值。注意这些并不会是我们免于遵守任何约束。例如,如果一个动作指定了SET DEFAULT
,但是默认值不满足外键约束,操作将会失败。
与ON DELETE
相似,同样有ON UPDATE
可以用在一个被引用列被修改(更新)的情况,可选的动作相同。在这种情况下,CASCADE
意味着被引用列的更新值应该被复制到引用行中。
正常情况下,如果一个引用行的任意一个引用列都为空,则它不需要满足外键约束。如果在外键定义中加入了MATCH FULL
,一个引用行只有在它的所有引用列为空时才不需要满足外键约束(因此空和非空值的混合肯定会导致MATCH FULL
约束失败)。如果不希望引用行能够避开外键约束,将引用行声明为NOT NULL
。
一个外键所引用的列必须是一个主键或者被唯一约束所限制。这意味着被引用列总是拥有一个索引(位于主键或唯一约束之下的索引),因此在其上进行的一个引用行是否匹配的检查将会很高效。由于从被引用表中DELETE
一行或者UPDATE
一个被引用列将要求对引用表进行扫描以得到匹配旧值的行,在引用列上建立合适的索引也会大有益处。由于这种做法并不是必须的,而且创建索引也有很多种选择,所以外键约束的定义并不会自动在引用列上创建索引。
更多关于更新和删除数据的信息请见第 6 章。外键约束的语法描述请参考CREATE TABLE。
5.3.6. 排他约束
排他约束保证如果将任何两行的指定列或表达式使用指定操作符进行比较,至少其中一个操作符比较将会返回否或空值。语法是:
CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) );
详见CREATE TABLE ... CONSTRAINT ... EXCLUDE
。
增加一个排他约束将在约束声明所指定的类型上自动创建索引。