CREATE DOMAIN — 定义一个新的域
CREATE DOMAINname[ AS ]data_type[ COLLATEcollation] [ DEFAULTexpression] [domain_constraint[ ... ] ] wheredomain_constraintis: [ CONSTRAINTconstraint_name] { NOT NULL | NULL | CHECK (expression) }
CREATE DOMAIN 创建一个新的域。域本质上是一种带有可选约束(对允许取值范围的限制)的数据类型。定义域的用户将成为它的所有者。
如果给定了模式名(例如 CREATE DOMAIN myschema.mydomain ...),则域会被创建在指定的模式中。否则它会创建在当前模式中。域的名字在其模式中已有的类型和域之中必须唯一。
域可用于把字段上的公共约束抽象到一处以便于维护。例如,多个表可能都包含电子邮件地址列,它们都需要相同的 CHECK 约束来验证地址语法。此时应定义一个域,而不是在每个表中单独设置约束。
要能够创建域,你必须对底层类型拥有 USAGE 特权。
name要创建的域的名字(可以用模式限定)。
data_type域的底层数据类型。其中可以包含数组说明符。
collation 域的一个可选排序规则。如果不指定排序规则,域将具有与其底层数据类型相同的排序行为。如果指定了 COLLATE,则底层类型必须是可排序的。
DEFAULT expression DEFAULT 子句为域数据类型的列指定一个默认值。该值可以是任何不含变量的表达式(但不允许使用子查询)。默认表达式的数据类型必须与域的数据类型匹配。如果不指定默认值,则默认值为空值。
任何未为该列指定取值的插入操作都会使用默认表达式。如果为某个特定列定义了默认值,它会覆盖与域相关联的任何默认值;而域的默认值又会覆盖与底层数据类型相关联的任何默认值。
CONSTRAINT constraint_name约束的可选名称。如果不指定,系统会生成一个名称。
NOT NULL该域的值不允许为空(但参见下文的注记)。
NULL该域的值允许为空。这是默认情况。
此子句仅用于与非标准 SQL 数据库保持兼容。不建议在新应用中使用它。
CHECK (expression) CHECK 子句指定该域的值必须满足的完整性约束或测试。每个约束必须是产生布尔结果的表达式。表达式中应使用关键字 VALUE 来引用被测试的值。求值为 TRUE 或 UNKNOWN 的表达式会通过。如果表达式产生 FALSE 结果,则会报告错误,且该值不允许被转换为该域的类型。
目前,CHECK 表达式不能包含子查询,也不能引用 VALUE 之外的变量。
当一个域具有多个 CHECK 约束时,将按名称的字母顺序进行测试。(PostgreSQL 9.5 之前的版本不保证 CHECK 约束的任何特定触发顺序。)
域约束(特别是 NOT NULL)会在把值转换为域类型时进行检查。尽管存在这样的约束,名义上属于域类型的列仍有可能读出为空。例如,在外连接查询中,如果域列位于外连接的可空一侧,就可能发生这种情况。一个更隐蔽的例子是
INSERT INTO tab (domcol) VALUES ((SELECT domcol FROM tab WHERE false));
空的标量子 SELECT 会产生一个被视为域类型的空值,因此不会对它进行进一步的约束检查,插入将会成功。
由于 SQL 的一般假设是空值是每种数据类型的有效值,因此很难避免这类问题。所以最佳实践是,把域的约束设计成允许空值,然后根据需要把列级 NOT NULL 约束应用到域类型的列上,而不是直接应用到域类型上。
PostgreSQL 假定 CHECK 约束的条件是不可变的,也就是说,对于相同的输入值它们总是给出相同的结果。正是基于这一假设,才只在值第一次被转换为域类型时检查 CHECK 约束,而在其他时候不检查。(这本质上与表 CHECK 约束的处理方式相同,如 Section 5.5.1 中所述。)
破坏这一假设的一种常见方式是在 CHECK 表达式中引用一个用户定义函数,然后更改该函数的行为。PostgreSQL 并不禁止这样做,但如果存在现在违反 CHECK 约束的已存储域类型值,它也不会察觉。这会导致之后的数据库转储和恢复失败。处理这种变更的推荐方式是删除该约束(使用 ALTER DOMAIN)、调整函数定义,然后重新添加约束,从而针对已存储的数据重新检查它。
确保域的 CHECK 表达式不会抛出错误也是一种好做法。
这个示例创建了 us_postal_code 数据类型,然后在表定义中使用该类型。这里使用正则表达式测试来验证值看起来像一个有效的美国邮政编码:
CREATE DOMAIN us_postal_code AS TEXT
CHECK(
VALUE ~ '^\d{5}$'
OR VALUE ~ '^\d{5}-\d{4}$'
);
CREATE TABLE us_snail_addy (
address_id SERIAL PRIMARY KEY,
street1 TEXT NOT NULL,
street2 TEXT,
street3 TEXT,
city TEXT NOT NULL,
postal us_postal_code NOT NULL
);
CREATE DOMAIN 命令符合 SQL 标准。
此命令中的 NOT NULL 语法是 PostgreSQL 的扩展。(对于非复合数据类型,符合标准的等价写法是 CHECK (VALUE IS NOT NULL)。不过,按照 the section called “注记” 的说明,这类约束在实践中最好还是避免使用。)NULL “约束”是 PostgreSQL 的扩展(另请参见 Compatibility)。