约束#

数据类型是限制我们可以在表里存储什么数据的一种方法。不过,对于许多应用来说,这种限制实在是太粗糙了。如,一个包含产品价格的列应该只接受正数。但是没有哪种标准数据类型只接受正数。另外一个问题是你可能需要根据其它列或者其它行的数据来约束列数据。比如,在一个包含产品信息的表中,每个产品编号都应该只有一行。

对于这些问题,SQL 允许你在列和表上定义约束。约束允许你对数据施加任意控制。如果用户企图在列里存储违反约束的数据,那么就会抛出一个错误。这种情况同时也适用于数值来自缺省值的情况。

备注

MAGMA表支持所有类型的约束。但HORC表不支持索引,因此也不支持唯一性约束、主键约束这些和索引相关的约束。

检查约束#

检查约束是最常见的约束类型。它允许你声明在某个列里的数值必须使一个布尔表达式为真。比如,要强制一个正数的产品价格,你可以用:

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)
);

头两个约束看上去很面熟。第三个使用了一个新的语法。它没有附着在某个列上,而是在逗号分隔的列定义列表中以一个独立行的形式出现。列定义和约束定义可以按照任意顺序列出。

我们称头两个约束是”列约束”,而第三个约束是”表约束”,因为它和任何一个列定义都是分离的。列约束也可以写成表约束,而反过来很可能不行,因为系统假设列约束只引用它所从属的列。 OushuDB 并不强制这条规则,但是如果你希望自己的表定义可以和其它数据库系统兼容,那么你最好还是遵循这条规则。上面的例子也可以这么写:

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)
);

我们还要注意的是,当约束表达式计算结果为真或 NULL 的时候,检查约束会被认为是满足条件的。 因为大多数表达式在含有 NULL 操作数的时候结果都是 NULL ,所以这些约束不能阻止列值为 NULL 。要确保一个列值不为 NULL ,可以使用下面介绍的非空约束。

备注

MAGMA格式和HORC格式的表均支持列约束。

非空约束#

非空约束只是简单地声明一列必须不能是 NULL。下面是一个例子:

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

一个非空约束总是写成一个列约束。非空约束在功能上等效于创建一个检查约束 CHECK (column_name IS NOT NULL),但在 OushuDB 里,创建一个明确的非空约束效率更高。缺点是你不能给它一个明确的名字。

当然,一个列可以有多个约束。只要一个接着一个写就可以了:

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

它们的顺序无所谓。顺序并不影响约束检查的顺序。

NOT NULL约束有个相反的约束:NULL约束。 它并不意味着该列必须是空,因为这样的列也没用。 它只是定义了该列可以为空的这个缺省行为。 在 SQL 标准里没有定义NULL约束,因此不应该在可移植的应用中使用它。 在 OushuDB 里面增加这个约束只是为了和其它数据库系统兼容。 不过,有些用户喜欢它,因为这个约束可以让他们很容易在脚本文件里切换约束。 比如,你可以从下面这样开始:

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

然后在需要的时候插入NOT关键字。

小技巧

在大多数数据库设计里,主要的列都应标记为非空。

备注

MAGMA格式和HORC格式的表均支持非空约束。

唯一性约束#

唯一约束确保一列或一组列中包含的数据在表中的所有行中是唯一的。语法是:

-- 写成列约束形式
CREATE TABLE products (
  product_no integer UNIQUE,
  name text,
  price numeric
);

-- 写成表约束形式
CREATE TABLE products (
  product_no integer,
  name text,
  price numeric,
  UNIQUE (product_no)
);

要为一组列定义唯一约束,请将其编写为表约束,列名以逗号分隔:

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
);

添加唯一约束将自动在约束中列出的列或列组上创建唯一的B树索引。 仅覆盖某些行的唯一性限制不能写成唯一约束,但可以通过创建唯一的部分索引来强制执行此类限制。

通常,如果表中有多行约束中包含的所有列的值相等,则违反唯一约束。 但是,在此比较中,两个null值(空值)永远不会被视为相等。 这意味着即使存在唯一约束,也可以在至少一个受约束的列中存储包含空值的重复行。 这种行为符合SQL标准,但其他SQL数据库可能不遵循此规则。 因此,在开发旨在可移植的应用程序时要小心。

备注

因为HORC表不支持索引,所以也不支持唯一性约束。MAMGA表支持唯一性约束。

主键#

理论上讲,主键约束等价于唯一约束和非空约束的结合。OshuDB的MAGMA表和HORC表均支持主键约束。

以下三种等价方式定义主键:

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 products (
    product_no integer,
    name text,
    price numeric,
    PRIMARY KEY (product_no)
);

主键约束可以同时约束多个列:

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

主键意味着表中的一列或一组列可以用作表中每行的唯一标识。(这是主键定义的直接结果)。主键约束与唯一约束不同的是,唯一约束不排除null值。这对于记录目的和用户程序都可用。比如,一个GUI应用程序可能需要知道表中的主键来唯一确定需要修改的行。

备注

大多数数据库都会为主键列创建主键索引,但由于HORC不支持索引,所以HORC表上不支持主键约束。MAGMA表支持索引,也支持主键约束。