您的位置:寻梦网首页编程乐园数据库PostgreSQL 7.2 Documentation

Chapter 6. 数组

PostgreSQL 允许记录的字段定义成 定长或不定长的多维数组.数组类型可以是任何基本类型或用户定义类型. 为说明这些用法,我们先创建一个由基本类型数组构成的表:

CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][]
);

如上所示,一个数组类型是通过在数组元素类型名后面附加方括弧 ( [] ) 来命名的. 上面的语句将创建一个叫 sal_emp 的表,它有一个 text 类型字符串字段 ( name ), 一个一维 integer 型数组 ( pay_by_quarter ), 代表雇员的季度薪水和一个两维 text 类型数组( schedule ), 表示雇员的周计划.

现在我们做一些 INSERT . 注意我们向数组字段追加数据时,观察我们写入数组数值的时候, 我们用花括弧把数值括起来并且用逗号将它们分开. 如果你懂 C,那么这与初始化一个结构很像。

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"meeting", "lunch"}, {}}');

INSERT INTO sal_emp
    VALUES ('Carol',
    '{20000, 25000, 25000, 25000}',
    '{{"talk", "consult"}, {"meeting"}}');

现在我们可以在 sal_emp 上运行一些查询。 首先,我们演示如何一次访问数组的一个元素. 这个查询检索在第二季度薪水变化的雇员名:

SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];

 name
-------
 Carol
(1 row)

数组的脚标数字是写在方括弧内的. PostgreSQL 缺省使用 "以1为基" 的数组习惯, 也就是说,一个 n 元素的数组从 array[1] 开始, 到 array[ n ] 结束.

这个查询检索所有雇员第三季度的薪水:

SELECT pay_by_quarter[3] FROM sal_emp;

 pay_by_quarter
----------------
          10000
          25000
(2 rows)

我们还可以访问一个数组的任意正方形片断,或称子数组. 对于一维或更多维数组,一个数组的某一部分是用 脚标下界 : 脚标上界 表示的。 下面查询检索 Bill 该周头两天的第一件计划.

SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';

      schedule
--------------------
 {{"meeting"},{""}}
(1 row)

我们还可以这样写

SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill';

获取同样的结果。如果任何脚标写成 lower : upper 的形式,那么任何数组脚标操作 都当做一个数组片断对待.小于 1 的范围在任何脚标中都假设为 只声明了一个数值.

一个数组值可以完全被代替:

UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
    WHERE name = 'Carol';

或者只是更新某一个域:

UPDATE sal_emp SET pay_by_quarter[4] = 15000
    WHERE name = 'Bill';

或者更新某个部分:

UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
    WHERE name = 'Carol';

我们可以通过给一个和已存在的元素相邻元素赋值的, 或者是向已存在的数据相邻或覆盖的区域赋值的方法来扩大一个数组. 比如,如果一个数组当前有 4 个元素,那么如果我们给array[5]赋值 后,它就有五个元素.目前,这样的扩大只允许多一维数组进行, 不能对多维数组进行操作.

CREATE TABLE 的语法允许定义固定长度的数组:

CREATE TABLE tictactoe (
    squares   integer[3][3]
);

不过,目前的实现并不强加数组尺寸限制 --- 这个行为与未声明长度数组一样。

实际上,目前的实现也不强制声明维数.某种数组元素类型的数组都被认为 是同一种(数组)类型,而并不管它们的尺寸和维数是多少.

目前的任何数组的维数都可以用 array_dims 函数检索出来:

SELECT array_dims(schedule) FROM sal_emp WHERE name = 'Carol';

 array_dims
------------
 [1:2][1:1]
(1 row)

array_dims 生成 text 结果, 这个结果可能便于人们读取但可能不便于编程.

要搜索一个数组中的数值,你必须检查该数组的每一个值. 你可以手工处理(如果你知道数组尺寸):

SELECT * FROM sal_emp WHERE pay_by_quarter[1] = 10000 OR
                            pay_by_quarter[2] = 10000 OR
                            pay_by_quarter[3] = 10000 OR
                            pay_by_quarter[4] = 10000;

不过,对于大数组而言,这个方法很快就会让人觉得无聊,并且如果你 不知道数组尺寸,那就没什么用了. 有一个 PostgreSQL 的扩展 (尽管它不是主 PostgreSQL 发布的一部分,) 它定义了几个函数用于迭代数组值.使用这个工具, 上面的查询可以是:

SELECT * FROM sal_emp WHERE pay_by_quarter[1:4] *= 10000;

要搜索整个数组(而不只是声明的列),你可以用:

SELECT * FROM sal_emp WHERE pay_by_quarter *= 10000;

另外,你可以用下面的语句找出所有数组有值等于 10,000 的行:

SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;

要安装这个可选的模块,看看 PostgreSQL 源程序版本的 contrib/array 目录.

小窍门: 数组不是集合;象我们前面那些段落里描述的那样使用数组 通常表明你的库设计有问题.数组字段通常是可以分裂成独立的表. 很明显表要容易搜索得多.

注意: 目前的数组的实现的缺点是一个数组的独立元素不能为 SQL 的 NULL. 你可以把整个数组设置为 NULL,但是你不能有那种一部分是 NULL,而另外 一部分不是 NULL 的数组.修补这个毛病的建议在 to-do 列表里.

引起数组元素. 如上所述,在书写一个数组的文本值的时候你要用双引号包围任意独立的 数组元素.如果元素数值可能令数组数值分析器产生歧意,那么你 必须 这么做. 比如,那些包含花括弧,逗号,双引号,反斜扛,或者空白的元素都必须加 双引号.要把双引号或者反斜扛放到数组元素值里,给它们加一个反斜扛前缀.

窍门: 请记住你在 SQL 查询里写的任何东西都将首先解释成一个字串文本, 然后才是一个数组.这样就造成你所需要的反斜扛数量翻了翻. 比如,要插入一个包含反斜扛和双引号的 text 数组, 你需要这么写

INSERT ... VALUES ('{"\\\\","\\""}');

字串文本处理器去掉第一层反斜扛,然后省下的东西到了数组数值分析器的 时候看起来象 {"\\","\""} .接着,该字串传递给 text 数据类型的输入过程,分别变成 \ " . (如果我们用的数据类型对反斜扛也有特殊待遇,比如 bytea , 那么我们可能需要在查询里放多达八个反斜扛才能在存储态的数组元素中 得到一个反斜扛.)