购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.11 复合类型

PostgreSQL中可以如C语言中的结构体一样定义一个复合类型。

5.11.1 复合类型的定义

下面先举例说明复合类型是如何定义的。

示例1,定义一个复数类型:


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

示例2,定义一个“person”类型:


CREATE TYPE person AS (
  name   text,
  age    integer,
sex    boolean
);

从上面的示例中我们可以看到,创建复合类型的语法类似于CREATE TABLE,只是这里只能声明字段名字和类型,目前不能声明约束(比如NOT NULL)。请注意,AS关键字很重要,没有它,系统会认为这是另一种完全不同的CREATE TYPE命令,因此你会看到奇怪的语法错误。

定义了复合类型后,就可以用此类型创建表了,示例如下:


CREATE TABLE capacitance_test_data(
  test_time timestamp,
  voltage     complex,
  current     complex
);

当然也可以使用此类型作为函数的参数,下面的示例中定义了前面创建的复数的乘法函数:


CREATE FUNCTION complex_multi(complex, complex ) RETURNS complex
  AS $$ SELECT ROW($1.r*$2.r - $1.i*$2.i, $1.r*$2.i - $1.i*$2.r)::complex $$
  LANGUAGE SQL;

5.11.2 复合类型的输入

复合类型常量的一般格式如下:


'( val1 , val2 , ... )'

从以上格式可以看出,其使用的是单引号加圆括号的一种格式。在此格式中,可以在任何字段值周围加上双引号,如果值本身包含逗号或者圆括弧,则必须用双引号括起来。

示例如下:


CREATE TYPE person AS (
  name   text,
  age    integer,
sex    boolean
);
CREATE TABLE author(
id int,
person_info person,
book         text
);
insert into author values( 1, '("张三",29,TRUE)', '张三的自传');

要让一个字段值是“NULL”,那么在列表里它的位置上就不要写任何字符。比如,以下常量在第三个字段声明了一个“NULL”:


osdba=# insert into author values( 2, '("张三",,TRUE)', '张三的自传');
INSERT 0 1

如果想要一个空字符串,而不是“NULL”,则需要写一对双引号:


insert into author values(3,'("",,TRUE)', 'x的自传');

也可以用ROW表达式语法来构造复合类型值。在大多数场合下,这种方法比用字符串文本的语法更简单,因为不用操心多重引号转义导致的问题。示例如下:


insert into author values( 4, ROW('张三', 29, TRUE), '自传');

只要表达式里有一个以上的字段,那么关键字ROW实际上也就是可选的,上面的语句可以简化为如下SQL语句:


insert into author values(5, ('张三', 29, TRUE), '自传');

5.11.3 访问复合类型

访问复合类型字段的一个域就如C语言中访问结构体中的一个成员一样,即写出一个点以及域的名字就可以了。这也非常像从一个表名字里选出一个字段。实际上,因为这实在太像从表名字中选取字段了,所以我们经常需要用圆括弧来避免SQL解析器的混淆。比如,你可能需要从person_info字段中选取一些子域,示例如下:


osdba=# select person_info.name from author;
ERROR:  missing FROM-clause entry for table "person_info"
LINE 1: select person_info.name from author;
               ^

但系统会报错,这时就需要在字段名称中加圆括号,具体如下:


osdba=# select (person_info).name from author;
  name 
------
  张三
  (1 rows)

或者也可以加上表名,具体如下:


select (author.person_info).name from author;

类似的语法问题适用于任何需要从一个复合类型值中查询一个域的情形。比如,要从一个返回复合类型值的函数中选取一个字段,SQL语句如下:


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

如果没有额外的圆括弧,就会产生语法错误。

5.11.4 修改复合类型

我们先来看插入或者更新整个字段的示例:


insert into author values( ('张三', 29, TRUE), '自传');
UPDATE author SET person_info = ROW('李四', 39, TRUE) WHERE id =1;
UPDATE author SET person_info = ('王二', 49, TRUE) WHERE id =2;

也可以只更新一个复合字段的某个子域:


UPDATE author SET person_info.name ='王二二' WHERE id =2;
UPDATE author SET person_info.age = (person_info).age + 1 WHERE id =2;

需要注意的是,不能在“SET”后的字段名周围加圆括弧,但是需要在等号右边的表达式里引用同一个字段的时候加上圆括弧,否则系统会报错:


osdba=# UPDATE author SET (person_info).name ='王二二' WHERE id =2;
ERROR:  syntax error at or near "."
LINE 1: UPDATE author SET (person_info).name ='王二二' WHERE id =2;
                                       ^ 
osdba=# UPDATE author SET person_info.age = person_info.age + 1 WHERE id =2;
ERROR:  missing FROM-clause entry for table "person_info"
LINE 1: UPDATE author SET person_info.age = person_info.age + 1 WHER...
                                            ^

INSERT也可以指定复合字段的子域,示例如下:


INSERT INTO author (id, person_info.name, person_info.age) VALUES(10, '张三',29);

在上面的例子中,因子域未为复合字段提供数值,故将用“NULL”填充。

5.11.5 复合类型的输入输出

在PostgreSQL中,每个基本类型都有相应的I/O转换解析规则,而在解析复合类型的文本格式时,会先解析由复合结构定义的圆括号和相邻域之间的逗号等包含的部分,其子域会用各自子域的I/O转换解析规则进行分析。示例如下:


'(  42)'

如果子域类型是整数,那么“42”前的空格将被忽略,但是如果是文本,那么该空格就不会被忽略。

在给一个复合类型写数值的时候,可以将独立的子域数值用双引号括起来,就像前面的示例一样,特别是当子域数值会导致复合数值分析器产生歧义时,就必须加双引号,比如,子域包含圆括弧、逗号、双引号、反斜杠的情形。要想在双引号括起来的子域数值里面放双引号,那么就需要在它前面放一个反斜杠。同样,在双引号括起来的子域数值里面的一对双引号表示一个双引号字符,类似于SQL字符串文本的单引号规则。另外,也可以用反斜杠进行转义,而不必用引号。

需要注意的是,写的任何SQL命令都会先被当作字符串文本来解析,然后才是复合类型。这样一来,所需要的反斜杠数目就加倍了。比如,要插入一个包含双引号和一个反斜杠的text子域到一个复合类型的数值里,SQL语句如下:


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

在上面的例子中,字符串文本处理器先吃掉一层反斜杠,使复合类型分析器中的内容变成“(“\”\\”)”。接着,将该字符串传递给text数据类型的输入过程,再吃掉一层反斜杠后内容变成我们需要的““\”。如果所使用的数据类型对反斜杠也有特殊意义,比如bytea类型,那么可能需要在命令里放多达8个的反斜杠,这样在存储的复合类型子域中才能有一个反斜杠。所以在SQL命令里写复合类型值的时候,ROW构造器通常比复合文本语法更易于使用。

如果子域数值是空字符串,或者包含圆括弧、逗号、双引号、反斜杠、空白,那么复合类型的输出程序会在子域数值周围加上双引号。 qNHFwYEdyR3bvzkSShkCfUNHnmv1FcwwnaWceC4ncOE+PIgDQbqD6JQj9WHqxNjI

点击中间区域
呼出菜单
上一章
目录
下一章
×