选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.0 已于 2015 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

8.15. 复合类型 #

复合类型表示一行或一条记录的结构;本质上它就是字段名及其数据类型的列表。PostgreSQL允许像使用简单类型那样,在许多相同的场合使用复合类型。例如,表中的一列可以声明为复合类型。

8.15.1. 复合类型的声明

下面是两个定义复合类型的简单示例:

CREATE TYPE complex AS (
    r       double precision,
    i       double precision
);

CREATE TYPE inventory_item AS (
    name            text,
    supplier_id     integer,
    price           numeric
);

这种语法与CREATE TABLE类似,但只能指定字段名和类型;目前还不能包含约束(例如NOT NULL)。注意,AS关键字必不可少;如果没有它,系统会认为你想使用另一种CREATE TYPE命令,并产生令人费解的语法错误。

定义了这些类型之后,我们可以用它们来创建表:

CREATE TABLE on_hand (
    item      inventory_item,
    count     integer
);

INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);

或者创建函数:

CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric
AS 'SELECT $1.price * $2' LANGUAGE SQL;

SELECT price_extension(item, 10) FROM on_hand;

每当创建一张表时,也会自动创建一个与该表同名的复合类型,用来表示表的行类型。例如,假如我们执行了:

CREATE TABLE inventory_item (
    name            text,
    supplier_id     integer REFERENCES suppliers,
    price           numeric CHECK (price > 0)
);

那么,与上文相同的inventory_item复合类型就会随之产生,而且可以像上文那样使用。不过,请注意当前实现的一个重要限制:由于复合类型本身不关联任何约束,表定义中的约束并不适用于表之外的复合类型值。(一种部分解决办法是使用域类型作为复合类型的成员。)

8.15.2. Composite Value Input

要把组合值写成字面常量,请将各字段值放在圆括号内,并用逗号分隔。你可以给任意字段值加双引号;如果它包含逗号或圆括号,则必须这样做。(更多细节见下文。)因此,组合常量的一般格式如下:

'( val1 , val2 , ... )'

An example is:

'("fuzzy dice",42,1.99)'

它是上面定义的inventory_item类型的一个合法值。要让某个字段为 NULL,在该字段在列表中的位置上什么也不写即可。例如,下面的常量指定第三个字段为 NULL:

'("fuzzy dice",42,)'

如果你想要的是空串而不是 NULL,请写双引号:

'("",42,)'

这里第一个字段是非 NULL 的空串,第三个字段是 NULL。

(这些常量实际上只是第 4.1.2.7 节中讨论的通用类型常量的一个特例。该常量最初被当作字符串处理,然后传递给组合类型的输入转换例程。有时可能需要显式指定类型。)

ROW表达式语法也可以用来构造组合值。在多数情况下,它比字符串字面量语法简单得多,因为你不必担心多层引号的问题。我们在上面已经用过这种方法:

ROW('fuzzy dice', 42, 1.99)
ROW('', 42, NULL)

只要表达式中的字段多于一个,ROW 关键字实际上可以省略,因此上面的例子可以简化为:

('fuzzy dice', 42, 1.99)
('', 42, NULL)

ROW表达式语法的更多细节见第 4.2.12 节

8.15.3. 访问复合类型

要访问组合列中的某个字段,可以写一个点号再加字段名,这很像通过表名选取字段。实际上,它与通过表名选取字段太像了,以至于你通常必须使用圆括号,以免让解析器混淆。例如,你可能尝试从示例表on_hand中选取一些子字段:

SELECT item.name FROM on_hand WHERE item.price > 9.99;

这不会起作用,因为根据 SQL 语法规则,名称item会被当成表名,而不是on_hand的列名。你必须写成这样:

SELECT (item).name FROM on_hand WHERE (item).price > 9.99;

或者,如果你还需要使用表名(例如在多表查询中),可以这样写:

SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;

现在,加上括号的对象就会被正确解释为对item列的引用,然后就可以从中选出子字段。

无论何时从组合值中选择字段,都会遇到类似的语法问题。例如,要从一个返回组合值的函数结果中只选取一个字段,你需要这样写:

SELECT (my_func(...)).field FROM ...

如果没有额外的圆括号,这将生成一个语法错误。

8.15.4. 修改组合值

下面是一些插入和更新组合列时正确语法的示例。先看插入或更新整个列值的情况:

INSERT INTO mytab (complex_col) VALUES((1.1,2.2));

UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;

第一个示例省略了ROW,第二个示例使用了它;两种写法都可以。

我们也可以更新组合列中的单个子字段:

UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;

注意,这里不需要(事实上也不能)给紧跟在SET后面的列名加圆括号,但在等号右侧的表达式中引用同一列时,则需要加圆括号。

我们也可以把子字段指定为INSERT的目标:

INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);

如果我们没有为该列的所有子字段提供值,其余子字段就会填充为 NULL 值。

8.15.5. 复合类型的输入和输出语法

组合值的外部文本表示由两部分组成:一部分是按照各字段类型的 I/O 转换规则解释的项,另一部分是表明组合结构的附加符号。这些附加符号包括包围整个值的圆括号(()),以及相邻项之间的逗号(,)。圆括号外部的空白会被忽略;但在圆括号内部,空白会被视为字段值的一部分,其是否有意义取决于该字段数据类型的输入转换规则。例如,在

'(  42)'

中,如果字段类型是 integer,则空白会被忽略;如果是 text,则不会被忽略。

如前所示,在写组合值时,你可以给任意单个字段值加双引号。如果字段值本身可能让组合值解析器混淆,则必须这样做。特别是,包含圆括号、逗号、双引号或反斜杠的字段必须用双引号括起来。要在带引号的组合字段值中写入双引号或反斜杠,需要在其前面加一个反斜杠。(另外,带双引号的字段值内部成对出现的双引号会被视为一个双引号字符,这与 SQL 字面字符串中单引号的规则类似。)或者,你也可以完全不使用引号,而改用反斜杠转义,保护所有原本会被当作组合语法的数据字符。

完全空的字段值(即逗号或圆括号之间一个字符也没有)表示 NULL。要写一个空字符串值而不是 NULL,可以写成""

如果字段值是空字符串,或者包含圆括号、逗号、双引号、反斜杠或空白字符,复合类型输出例程就会在其周围加上双引号。(对空白字符这样做并非必需,但有助于提高可读性。)嵌入字段值中的双引号和反斜杠会被加倍。

注意

请记住,你在 SQL 命令中写的内容会先被解释为字符串字面量,然后再被解释为组合值。这使你需要的反斜线数量翻倍(假定使用转义字符串语法)。例如,要在组合值中插入一个包含双引号和反斜线的text字段,你需要写:

INSERT ... VALUES (E'("\\\"\\\\")');

字符串字面量处理器会去掉一层反斜线,因此到达组合值解析器的内容看起来像("\"\\")。相应地,传给text数据类型输入例程的字符串变成"\。(如果我们使用的是输入例程也特殊处理反斜线的类型,例如bytea,则要在命令中写多达八个反斜线才能在存储的组合字段中得到一个反斜线。)可以使用美元引用(见第 4.1.2.4 节)来避免双写反斜线。

提示

在 SQL 命令中编写组合值时,ROW构造器语法通常比组合字面量语法更容易使用。在ROW中,各个字段值的写法与它们不是组合成员时完全相同。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。