Chapter 6. 数组

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

CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][]
);
上面的语句将创建一个叫 sal_emp 的表,它有一个 text 类型字符串字段 (name), 一个一维 int4型数组 (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)
Postgres 缺省使用"以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 的 contrib 目录里有一个 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 目录.

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