打印

PostgreSQL学习文档 8.0

8.5.1.2. 时间

当日时间类型是 time [ (p) ] without time zone 和 time [ (p) ] with time zone。 只写 time 等效于 time without time zone。

这些类型的有效输入由当日时间后面跟着可选的时区组成。 (参阅 Table 8-11。) 如果在 time without time zone 类型的输入 中声明了时区,那么它会被无声地忽略。

Table 8-11. 时间输入
例子        描述
04:05:06.789        ISO 8601
04:05:06        ISO 8601
04:05        ISO 8601
040506        ISO 8601
04:05 AM        与 04:05 一样;AM 不影响数值
04:05 PM        与 16:05一样;输入小时数必须 <= 12
04:05:06.789-8        ISO 8601
04:05:06-08:00        ISO 8601
04:05-08:00        ISO 8601
040506-08        ISO 8601
04:05:06 PST        用名字声明的时区

Table 8-12. 时区输入
例子        描述
PST        太平洋标准时间(Pacific Standard Time)
-8:00        ISO-8601 与 PST 的偏移
-800        ISO-8601 与 PST 的偏移
-8        ISO-8601 与 PST 的偏移
zulu        军方对 UTC 的缩写(译注:可能是美军)
z        zulu 的缩写

参考Appendix B 获取可以识别的时区输入。
8.5.1.3. 时间戳

时间戳类型的有效输入由一个日期和时间的联接组成,后面跟着一个可选的时区,一个可选的 AD 或者 BC。(另外,AD/BC 可以出现在时区前面,但这个顺序并非最佳的。) 因此

1999-01-08 04:05:06



1999-01-08 04:05:06 -8:00

是一个有效的数值, 它是兼容 ISO 8601 的。另外,下面这种使用广泛的格式

January 8 04:05:06 1999 PST

也受支持。

SQL 标准通过 "+" 或者 "-" 是否存在来区分 timestamp without time zone 和 timestamp with time zone 文本。 因此,根据标准,

TIMESTAMP '2004-10-19 10:23:54'

是一个 timestamp without time zone, 而

TIMESTAMP '2004-10-19 10:23:54+02'

是一个 timestamp with time zone。 PostgreSQL 与标准不同的地方是要求必须明确键入 timestamp with time zone 文本:

TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'

。 如果一个文本不是明确地以 timestamp with time zone 开头,PostgreSQL 将不声不响忽略任何文本中指明的失去。 因此,生成的日期/时间值是从输入值的日期/时间字段衍生出来的,并且没有就时区进行调整。

对于 timestamp [without time zone],任何在输入中声明的时区都被悄悄吞掉。 也就是说,生成的日期/时间数值是从输入中明确的日期/时间字段中得出的,并且没有根据时区调整。

对于 timestamp with time zone,内部存储的数值总是 UTC (全球统一时间,以前也叫格林威治时间GMT)。如果一个输入值有明确的时区声明, 那么它将用该时区合适的偏移量转换成 UTC。如果在输入字串里没有时区声明, 那么它就假设是在系统的 timezone 参数里的那个时区,然后使用这个 timezone 时区转换成 UTC。

如果输出一个 timestamp with time zone,那么它总是从 UTC 转换成当前的 timezone 时区,并且显示为该时区的本地时间。 要看其它时区的该时间,要么修改 timezone,要么使用 AT TIME ZONE 构造(参阅 Section 9.9.3)。

在 timestamp without time zone 和 timestamp with time zone 之间的转换通常假设 timestamp without time zone 数值应该以 timezone 本地时间的形式接受或者写出。 其它的时区引用可以用 AT TIME ZONE 的方式为转换声明。

TOP

8.5.1.4. 间隔

interval数值可以用下面语法声明:

[@] quantity unit [quantity unit...] [direction]

这里:quantity 是一个数字(可能有符号); unit 是 second, minute, hour, day, week, month, year, decade, century, millennium, 或者这些单位的缩写或复数; direction 可以是 ago 或者为空。符号 @ 是一个可选的东西。不同的单位以及相应正确的符号都是隐含地增加的。

日期,小时,分钟,以及秒钟的数量可以在无明确单位标记的情况下声明。 比如,'1 12:59:10' 和 '1 day 12 hours 59 min 10 sec' 读数一样。

可选的精度 p 应该介于 0 和 6 之间, 并且缺省是输入文本的精度。
8.5.1.5. 特殊值

PostgreSQL 为方便起见支持几个特殊输入值, 如在 Table 8-13 里面显示的那样。 值infinity 和 -infinity 是特别在系统内部表示的,并且将按照同样的方式显示; 但是其它的都只是符号缩写,在读取的时候将被转换成普通的日期/时间值。 (特别是,now 和相关的字串在读取的时候就被转换成对应的数值。) 所有这些值在 SQL 命令里当作普通常量对待时,都需要写在单引号里面。

Table 8-13. 特殊日期/时间输入
输入字串        有效类型        描述
epoch        date, timestamp        1970-01-01 00:00:00+00 (Unix 系统零时)
infinity        timestamp        比任何其它时间戳都晚
-infinity        timestamp        比任何其它时间戳都早
now        date, time, timestamp        当前事务时间
today        date, timestamp        今日午夜
tomorrow        date, timestamp        明日午夜
yesterday        date, timestamp        昨日午夜
allballs        time        00:00:00.00 UTC

下列 SQL 兼容函数也可以用于获取对应数据类型的当前时间值: CURRENT_DATE,CURRENT_TIME, CURRENT_TIMESTAMP,LOCALTIME, LOCALTIMESTAMP。最后四个接受一个可选的精度声明。 (见 Section 9.9.4 。) 不过,请注意这些 SQL 函数不是被当作数据输入串识别的。
8.5.2. 日期/时间输出

使用 SET DateStyle,时间/日期类型的输出格式可以设成四种风格之一: ISO 8601,SQL (Ingres),传统的 POSTGRES,和 German 。缺省是 ISO 格式。 (SQL 标准要求使用 ISO 8601 格式。"SQL" 输出格式的名字是历史偶然。)Table 8-14 显示了每种输出风格的例子。date 和 time 类型的 输出当然只是给出的例子里面的日期和时间部分。

Table 8-14. 日期/时间输出风格
风格描述        描述        例子
ISO        ISO-8601/SQL 标准        1997-12-17 07:37:16-08
SQL        传统风格        12/17/1997 07:37:16.00 PST
POSTGRES        原始风格        Wed Dec 17 07:37:16 1997 PST
German        地区风格        17.12.1997 07:37:16.00 PST

如果声明了 DMY 字段,那么在 SQL 和 POSTGRES 风格里,日期在月份之前出现,否则月份出现在日期之前。 (参阅 Section 8.5.1 部分,看看这个设置是如何影响对输入值的解释。) Table 8-15 显示了一个例子。

Table 8-15. 日期顺序习惯
风格描述        描述        例子
SQL, DMY        日/月/年        17/12/1997 15:37:16.00 CET
SQL, MDY        月/日/年        12/17/1997 07:37:16.00 PST
Postgres, DMY        day/month/year        Wed 17 Dec 07:37:16 1997 PST

interval 的输出看起来象输入格式,只是象 century 和 week 这样的单位被转换成年和日,而 ago 被转换成合适的符号。在 ISO 模式下输出看起来象

[ quantity unit [ ... ] ] [ days ] [ hours:minutes:secondes ]

日期/时间风格可以由用户用 SET datestyle 命令 选取,或者在 postgresql.conf 配置文件里的参数 DateStyle 设置,或者服务器或客户端的 PGDATESTYLE 环境变量里设置。我们也可以用格式化函数 to_char(参阅 Section 9.8) 来更灵活地控制时间/日期地输出。
8.5.3. 时区

时区和时区习惯不仅仅受地球几何形状的影响,还受到政治决定的影响。 到了19世纪,全球的时区变得稍微标准化了些,但是还是易于遭受随意的修改, 部分是因为夏时制规则。 PostgreSQL 目前支持 1902 年到 2038 年之间的夏时制信息(对应于传统 Unix 系统时间的完整跨度)。 如果时间超过这个范围,那么假设时间是选取的时区的"标准时间",不管它们落在哪个年份里面。

PostgreSQL 在典型应用中尽可能与 SQL 的定义相兼容。但 SQL 标准在日期和时间类型和功能上有一些奇怪的混淆。两个显而易见的问题是:

    *

      date (日期)类型与时区没有联系,而 time (时间)类型却有或可以有。 然而,现实世界的时区只有在与时间和日期都关联时才有意义, 因为时间偏移量(时差)可能因为实行类似夏时制这样的制度而在一年里有所变化。
    *

      缺省的时区用一个数字常量表示与UTC的偏移(时差)。 因此,当跨 DST 界限做日期/时间算术时, 我们根本不可能把夏时制这样的因素计算进去。

为了克服这些困难,我们建议在使用时区的时候,使用那些同时包含日期和时间的日期/时间类型。 我们建议不要使用类型 time with time zone (尽管 PostgreSQL 出于合理应用以及为了与其他RDBMS实现兼容的考虑支持这个类型)。 PostgreSQL 假设你用于任何类型的本地时区都只包含日期或时间。

在系统内部,所有日期和时间都是用全球统一时间UTC格式存储, 时间在发给客户前端前由数据库服务器转换成本地时间,使用的是配置参数 timezone 声明的时区。

我们可以在 postgresql.conf 文件里设置配置参数 timezone, 或者用任何其它在 Section 16.4 描述的标准方法。 还有好几种特殊方法可以设置它:

    *

      如果没有在 postgresql.conf 里声明 timezone,也没有在命令行开关上声明, 服务器试图使用服务器主机上的TZ环境变量作为服务器的缺省时区。 如果没有定义 TZ,或者是 PostgreSQL 不认识的时区名, 那么服务器将试图通过检查 C 库函数 localtime() 的行为来判断操作系统的缺省时区。 缺省时区是按照最接近 PostgreSQL 的已知时区的原则来选择的。
    *

      SQL 命令 SET TIME ZONE 为会话设置时区,这是 SET TIMEZONE TO 的一个可选的拼写方式, 更加兼容标准。
    *

      如果在客户端设置了PGTZ环境变量, 那么libpq在联接时将使用这个环境变量给后端发送一个 SET TIME ZONE 命令。

请参考 Appendix B 获取一个可用时区的列表。
8.5.4. 内部

PostgreSQL 使用儒略历法用于所有日期/时间计算。 如果假设一年的长度是365.2425天时,这个方法可以 很精确地预计/计算从4713 BC(公元前4713年)到很久的未来的任意一天的日期。

19世纪以前的日期传统(历法)只是对一些趣味读物有意义, 而在我们这里好象没有充分的理由把它们编码入日期/时间控制器里面去

TOP

8.6. 布尔类型

PostgreSQL 支持标准的 SQL boolean 数据类型。 boolean 只能有两个状态之一: '真'('True') 或 '假'('False')。 第三种状态,'未知'('Unknow'),用 SQL空状态表示。

"真"值的有效文本值是:

TRUE
't'
'true'
'y'
'yes'
'1'

而对于"假"而言,你可以使用下面这些:

FALSE
'f'
'false'
'n'
'no'
'0'

使用TRUE和FALSE 这样的字眼比较好(也是SQL兼容的用法)。

Example 8-2. 使用 boolean 类型

CREATE TABLE test1 (a boolean, b text);
INSERT INTO test1 VALUES (TRUE, 'sic est');
INSERT INTO test1 VALUES (FALSE, 'non est');
SELECT * FROM test1;
a |    b
---+---------
t | sic est
f | non est

SELECT * FROM test1 WHERE a;
a |    b
---+---------
t | sic est

Example 8-2 显示了使用字母 t 和 f 输出 boolean 数值的例子。

    技巧: boolean 类型的数值不能直接转换成其它 类型(也就是说, CAST (boolval AS integer) 是不会起作用的)。你可以用 CASE 表达式实现这个目的: CASE WHEN boolval THEN 'value if true' ELSE'value if false' END。见 Section 9.13。

bool 使用1字节存储空间。

TOP

8.7. 几何类型

几何数据类型表示二维的平面物体。 Table 8-16 显示了PostgreSQL 里面可以用的几何类型。 最基本的类型:点,是其他类型的基础。

Table 8-16. 几何类型
名字        存储空间        描述        表现形式
point        16 字节        空间中一点        (x,y)
line        32 字节        (无穷)直线(未完全实现)        ((x1,y1),(x2,y2))
lseg        32 字节        (有限)线段        ((x1,y1),(x2,y2))
box        32 字节        长方形        ((x1,y1),(x2,y2))
path        16+16n 字节        闭合路径(与多边形类似)        ((x1,y1),...)
path        16+16n 字节        开放路径        [(x1,y1),...]
polygon        40+16n 字节        多边形(与闭合路径相似)        ((x1,y1),...)
circle        24 字节        圆(圆心和半径)        <(x,y),r>

我们有一系列丰富的函数和操作符可用来进行各种几何计算, 如拉伸,转换,旋转和计算相交等。 它们在 Section 9.10 里有解释。
8.7.1. Point(点)

点是几何类型的基本二维构造单位。 用下面语法描述 point 的数值:

( x , y )
  x , y

这里的参数是 是用浮点数表示的点的 x 坐标和 y 坐标。
8.7.2. 线段

线段 (lseg)是用一对点来代表的。 lseg 的值用下面语法声明:

( ( x1 , y1 ) , ( x2 , y2 ) )
  ( x1 , y1 ) , ( x2 , y2 )  
    x1 , y1   ,   x2 , y2

这里的 (x1,y1), (x2,y2) 是线段的端点。
8.7.3. Box(方)

方是用一对对角点来表示的。 box 的值用下面语法声明:

( ( x1 , y1 ) , ( x2 , y2 ) )
  ( x1 , y1 ) , ( x2 , y2 )  
    x1 , y1   ,   x2 , y2

这里的 (x1,y1) 和 (x2,y2) 是方形的一对对角点。

方的输出使用第一种语法。在输入时将按先右上角后左下角的顺序重新排列。 你也可以输入其他的一对对角点。 但输入时将先从输入中和存储的角中计算出左下角和右上角然后再存储。
8.7.4. Path(路径)

路径由一系列连接的点组成。路径可能是开放的, 也就是认为列表中第一个点和最后一个点没有连接, 也可能是闭合的,这时认为第一个和最后一个点连接起来。

path 的数值用下面语法声明:

( ( x1 , y1 ) , ... , ( xn , yn ) )
[ ( x1 , y1 ) , ... , ( xn , yn ) ]
  ( x1 , y1 ) , ... , ( xn , yn )  
  ( x1 , y1   , ... ,   xn , yn )  
    x1 , y1   , ... ,   xn , yn   

这里的点是组成路径的线段的端点。 方括弧([])表明一个开放的路径,圆括弧(())表明一个闭合的路径。

路径的输出使用第一种语法输出。
8.7.5. Polygon(多边形)

多边形由一系列点代表(多边形的顶点)。多边形可以认为与闭合路径一样, 但是存储方式不一样而且有自己的一套支持过程/函数。

polygon 的数值用下列语法声明:

( ( x1 , y1 ) , ... , ( xn , yn ) )
  ( x1 , y1 ) , ... , ( xn , yn )  
  ( x1 , y1   , ... ,   xn , yn )  
    x1 , y1   , ... ,   xn , yn   

这里的点是组成多边形边界的线段的端点。

多边形输出使用第一种语法。
8.7.6. Circle(圆)

圆由一个圆心和一个半径代表。 circle 的数值用下面语法表示:

< ( x , y ) , r >
( ( x , y ) , r )
  ( x , y ) , r  
    x , y   , r  

这里的 (x,y) 是圆心,而r圆的半径

圆的输出用第一种格式。

TOP

8.8. 网络地址数据类型

PostgreSQL 提供用于存储 IPv4,IPv6 和 MAC 地址的数据类型, 在 Table 8-17 里显示。 用这些数据类型存储网络地址比用纯文本类型好,因为这些类型提供输入错误检查和好些种特殊的操作和功能。 (见 Section 9.11)。

Table 8-17. 网络地址类型
名字        存储空间        描述
cidr        12 或 24 字节        IPv4 和 IPv6 网络
inet        12 或 24 字节        IPv4 或 IPv6 网络和主机
macaddr        6 字节        MAC 地址

在对 inet 或者 cidr 数据类型进行排序的时候, IPv4 地址将总是排在 IPv6 地址前面,包括那些封装或者是映射在 IPv6 地址里 的 IPv4 地址,比如 ::10.2.3.4 或者 ::ffff::10.4.3.2。
8.8.1. inet

inet 在一个数据域里保存一个主机 IPv4 或 IPv6 地址, 以及一个可选的它所处的等效的子网。 子网的等效是通过计算主机地址中有多少位表示网络地址的方法来 表示的 ("网络掩码")。 如果网络掩码是 32 并且地址是 IPv4 ,那么不表示任何子网,只是一台主机。 在 IPv6 里,地址长度是 128 位,因此 128 位表明一个唯一的主机地址。 请注意如果你想只接受网络地址,你应该使用 cidr 类型而不是 inet。

该类型的输入格式是 地址/y 这里 地址 是 IPv4 或者 IPv6 主机,y 是网络掩码的位数。 如果 /y 部分未填, 则网络掩码对 IPv 而言是 32,对 IPv6 而言是 128, 所以该值表示只有一台主机。 显示时,如果 /y 部分是 /32,将不会显示出来。
8.8.2. cidr

cidr 保存一个 IPv4 或 IPv6 网络地址声明。 其输入和输出遵循无类的互联网域路由(Classless Internet Domain Routing)习惯。 声明一个网络的格式是 地址/y 这里 地址 是 IPv4 或 IPv6 网络地址而 /y 是 网络掩码的二进制位数。 如果省略 /y, 那么掩码部分用旧的有类的网络编号系统进行计算,但要求输入的数据已经包括了确定掩码的所需的所有字节。 如果声明了一个网络地址,它的指定掩码的右边置了位,那么算错误。

Table 8-18是些例子:

Table 8-18. cidr 类型输入举例
cidr 输入        cidr 显示        abbrev(cidr)
192.168.100.128/25        192.168.100.128/25        192.168.100.128/25
192.168/24        192.168.0.0/24        192.168.0/24
192.168/25        192.168.0.0/25        192.168.0.0/25
192.168.1        192.168.1.0/24        192.168.1/24
192.168        192.168.0.0/24        192.168.0/24
128.1        128.1.0.0/16        128.1/16
128        128.0.0.0/16        128.0/16
128.1.2        128.1.2.0/24        128.1.2/24
10.1.2        10.1.2.0/24        10.1.2/24
10.1        10.1.0.0/16        10.1/16
10        10.0.0.0/8        10/8
10.1.2.3/32        10.1.2.3/32        10.1.2.3/32
2001:4f8:3:ba::/64        2001:4f8:3:ba::/64        2001:4f8:3:ba::/64
2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128        2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128        2001:4f8:3:ba:2e0:81ff:fe22:d1f1
::ffff:1.2.3.0/120        ::ffff:1.2.3.0/120        ::ffff:1.2.3/120
::ffff:1.2.3.0/128        ::ffff:1.2.3.0/128        ::ffff:1.2.3.0/128
8.8.3. inet 与 cidr 对比

inet 和 cidr 类型之间的基本区别是 inet 接受右边有非零位的网络掩码, 而 cidr 不接受。

    提示: 如果你不喜欢 inet 或 cidr 值的输出 格式,请试一下 host ,text 和 abbrev 函数。

8.8.4. macaddr

macaddr 类型存储 MAC 地址,也就是以太网卡硬件地址 (尽管 MAC 地址还用于其它用途)。可以接受多种客户化的格式, 包括

'08002b:010203'
'08002b-010203'
'0800.2b01.0203'
'08-00-2b-01-02-03'
'08:00:2b:01:02:03'

它们声明的都是同一个地址。 对于数据位 a 到 f,大小写都行。 输出总是我们上面给出的最后一种形式。

在 PostgreSQL 源代码发布的 contrib/mac 目录里有一些可以将 MAC 地址映射为硬件制造商的名字的工具。

TOP

8.9. 位串类型

位串就是一串 1 和 0 的字串。它们可以用于存储和视觉化位掩码。 我们有两种类型的 SQL 位类型: bit(n) 和 bit varying(n); 这里的 n是一个正整数。

bit类型的数据必须准确匹配长度n; 试图存储短些或者长一些的数据都是错误的。类型 bit varying 数据是最长 n 的变长类型; 更长的串会被拒绝。写一个没有长度的 bit 等效于 bit(1),没有长度的bit varying 意思是没有长度限制。

    注意: 如果我们明确地把一个位串值转换成 bit(n), 那么它的右边将被截断或者在右边补齐零,直到刚好 n 位, 而不会抛出任何错误。类似地,如果我们明确地把一个位串数值转换成 bit varying(n),如果它超过了n 位, 那么它的右边将被截断。

    注意: 在PostgreSQL7.2 之前,不管是否有明确的转换, bit 都会在右边自动截断或者在在右边填充零的。这个行为现在已经为了和SQL标准兼容修改过来了。

请参考 Section 4.1.2.3 获取有关位串常量的语法的信息。还有一些位逻辑操作符和位处理函数可用; 见Section 9.6。

Example 8-3. 使用位串类型

CREATE TABLE test (a bit(3), b bit varying(5));
INSERT INTO test VALUES (B'101', B'00');
INSERT INTO test VALUES (B'10', B'101');
ERROR:  Bit string length 2 does not match type bit(3)
INSERT INTO test VALUES (B'10'::bit(3), B'101');
SELECT * FROM test;
  a  |  b
-----+-----
101 | 00
100 | 101

TOP

8.10. 数组

PostgreSQL 允许记录的字段定义成定长或不定长的多维数组。 数组类型可以是任何基本类型或用户定义类型。(不过,复合类型和域的数组还不支持。)
8.10.1. 数组类型的声明

为说明这些用法,我们先创建一个由基本类型数组构成的表:

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

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

CREATE TABLE 的语法允许声明数组的确切大小,比如:

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

不过,目前的实现并不强制数组尺寸限制 — 其行为和用于未声明长度的 数组相同。

实际上,目前的声明也不强制数组维数。特定元素类型的数组都被认为是相同的类型, 不管他们的大小或者维数。因此,在 CREATE TABLE 里定义数字或者维数都只是简单的文档,它并不影响运行时的行为。

另外还有一种语法,它遵循 SQL: 1999 标准,可以用于声明一维数组。 pay_by_quarter 可以定义为:

    pay_by_quarter  integer ARRAY[4],

这个语法要求一个整数常量表示数组尺寸。不过,和以前一样,PostgreSQL 并不强制这个尺寸限制。
8.10.2. 数组值输入

把一个数组数值写成一个文本值的时候, 我们用花括弧把数值括起来并且用逗号将它们分开。 (如果你懂 C,那么这与初始化一个结构很像。) 你可以在任何数组值周围放置双引号,如果这个值包含逗号或者花括弧, 那么你就必须加上双引号。(下面有更多细节。)因此,一个数组常量的常见格式如下:

'{ val1 delim val2 delim ... }'

这里的 delim 是该类型的分隔符, 就是那个在它的 pg_type 记录里指定的那个。 在 PostgreSQL 发布提供的标准数据类型里, 类型 box 使用分号(;),但是所有其它类型都用逗号(,)。 每个 val 要么是一个数组元素类型的常量,要么是一个子数组。一个数组常量的例子是

'{{1,2,3},{4,5,6},{7,8,9}}'

这个常量是一个两维的,3乘3的数组,由三个整数子数组组成。

(这种数组常量实际上只是我们在 Section 4.1.2.5 里讨论过的一般类型常量的一种特例。常量最初是当作字串看待并且传递给数组输入转换过程。可能需要我们用明确的类型声明。)

现在我们可以显示一些 INSERT 语句。

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"meeting", "lunch"}, {"meeting"}}');
ERROR:  multidimensional arrays must have array expressions with matching dimensions

请注意多维数组必须匹配每个维的元素数。如果不匹配则导致错误发生。

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

INSERT INTO sal_emp
    VALUES ('Carol',
    '{20000, 25000, 25000, 25000}',
    '{{"breakfast", "consulting"}, {"meeting", "lunch"}}');

目前的数组实现的一个局限是一个数组的独立元素不能是 SQL 空值。 整个数组可以设置为空,但是你不能有这么一个数组,里面有些元素是空, 而有些不是。

前面的两个插入的结果看起来像这样:

SELECT * FROM sal_emp;
name  |      pay_by_quarter       |                 schedule
-------+---------------------------+-------------------------------------------
Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
(2 rows)

我们还可以使用 ARRAY 构造器语法:

INSERT INTO sal_emp
    VALUES ('Bill',
    ARRAY[10000, 10000, 10000, 10000],
    ARRAY[['meeting', 'lunch'], ['training', 'presentation']]);

INSERT INTO sal_emp
    VALUES ('Carol',
    ARRAY[20000, 25000, 25000, 25000],
    ARRAY[['breakfast', 'consulting'], ['meeting', 'lunch']]);

请注意数组元素是普通的 SQL 常量或者表达式;比如,字串文本是用单引号包围的, 而不是像数组文本那样用双引号。ARRAY 构造器语法在 Section 4.2.10 里有更详细的讨论。

TOP

8.10.3. 访问数组

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

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

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

数组的脚标数字是写在方括弧内的。 PostgreSQL 缺省使用以一为基的数组习惯, 也就是说,一个 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},{training}}
(1 row)

我们还可以这样写

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

获取同样的结果。如果任何脚标写成 lower: upper 的形式,那么任何数组脚标操作总是当做一个数组片断对待。 如果只声明了一个数值,那么都是假设下界为 1,比如:

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

                 schedule
-------------------------------------------
{{meeting,lunch},{training,presentation}}
(1 row)

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

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

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

array_dims 生成一个 text 结果, 对于人类可能比较容易阅读,但是对于程序可能就不那么方便了。 我们也可以用 array_upper 和 array_lower 函数检索,它们分别返回指定数组维的上界和下界。

SELECT array_upper(schedule, 1) FROM sal_emp WHERE name = 'Carol';

array_upper
-------------
           2
(1 row)

TOP

8.10.4. 修改数组

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

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

或者使用 ARRAY 表达式语法:

UPDATE sal_emp SET pay_by_quarter = ARRAY[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';

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

数组片段赋值允许创建不使用一为基的下标的数组。 比如,我们可以给 array[-2:7] 赋值, 创建一个脚标值在 -2 和 7 之间的数组。

新的数组值也可以用连接操作符 || 构造。

SELECT ARRAY[1,2] || ARRAY[3,4];
?column?
-----------
{1,2,3,4}
(1 row)

SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
      ?column?
---------------------
{{5,6},{1,2},{3,4}}
(1 row)

连接操作符允许把一个元素压入一个一维数组的开头或者结尾。它还接受两个 N 维的数组,或者一个 N 维和一个 N+1 维的数组。

在向一个一维数组的开头压入一个元素后,结果是这样的一个数组: 它的低界下标等于右手边操作数的低界下标减一,如果向一个一维数组的结尾压入一个元素, 结果数组就是一个保持左手边操作数低界的数组。比如:

SELECT array_dims(1 || ARRAY[2,3]);
array_dims
------------
[0:2]
(1 row)

SELECT array_dims(ARRAY[1,2] || 3);
array_dims
------------
[1:3]
(1 row)

如果两个相同维数的数组连接在一起,结果保持左手边操作数的外层维数的低界下标。 结果是这样一个数组:它包含左手边操作数的每个元素,后面跟着右手边操作数的每个元素。比如:

SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]);
array_dims
------------
[1:5]
(1 row)

SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]);
array_dims
------------
[1:5][1:2]
(1 row)

如果一个 N 维的数组压到一个 N+1 维数组的开头或者结尾, 结果和上面的数组元素的情况类似。每个 N 维的子数组实际上都是 N+1 维数组的外层维数。比如:

SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);
array_dims
------------
[0:2][1:2]
(1 row)

数组也可以用函数 array_prepend, 和 array_append, 以及 array_cat 构造。头两个只支持一维数组, 而 array_cat 支持多维数组。 请注意使用上面讨论的连接操作符要比直接使用这些函数好。实际上, 这些函数主要用于实现连接操作符。不过,在用户定义的创建函数里直接使用他们可能有必要。一些例子:

SELECT array_prepend(1, ARRAY[2,3]);
array_prepend
---------------
{1,2,3}
(1 row)

SELECT array_append(ARRAY[1,2], 3);
array_append
--------------
{1,2,3}
(1 row)

SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
array_cat
-----------
{1,2,3,4}
(1 row)

SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
      array_cat
---------------------
{{1,2},{3,4},{5,6}}
(1 row)

SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
      array_cat
---------------------
{{5,6},{1,2},{3,4}}

TOP

8.10.5. 在数组中检索

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

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;

不过,对于大数组而言,这个方法很快就会让人觉得无聊,并且如果你不知道数组尺寸,那就没什么用了。 另外一个方法在 Section 9.17 里描述。 上面的查询可以用下面的代替:

SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter);

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

SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);

    提示: 数组不是集合;象我们前面那些段落里描述的那样使用数组通常表明你的库设计有问题。 数组字段通常是可以分裂成独立的表。 很明显表要容易搜索得多。并且在元素数目非常庞大的时候也可以更好地伸展。

8.10.6. 数组输入和输出语法

一个数组值的外部表现形式由一些根据该数组元素类型的 I/O 转换规则分析的项组成, 再加上一些标明该数组结构的修饰。 这些修饰由围绕在数组值周围的花括弧({ 和 }), 加上相临项之间的分隔字符组成。分隔字符通常是一个逗号(,), 但也可以是其它的东西:它由该数组元素类型的 typdelim 设置决定。 (在 PostgreSQL 版本提供的标准数据类型里, 类型 box 使用分号(;),但所有其它的类型使用逗号。) 在多维数组里,每个维(行,面,体,等)有自己级别的花括弧,并且在同级相临的花括弧项之间必须写分隔符。

如果数组元素值是空字串或者包含花括弧,分隔符,双引号,反斜杠或者空白, 那么数组输出过程将在这些值周围包围双引号。在元素值里包含的双引号和反斜杠将被反斜杠逃逸。 对于数值数据类型,你可以安全地假设数值没有双引号包围,但是对于文本类型,我们就需要准备好面对有双引号包围, 和没有双引号包围两种情况了。(这是对 7.2 以前的 PostgreSQL 版本的行为的一个改变。)

缺省时,一个数组的某维的下标索引是设置为一的。如果一个数组的某维的下标不等于一, 那么就会在数组结构修饰域里面放置一个实际的维数。这个修饰由方括弧([]) 围绕在每个数组维的下界和上界索引,中间有一个冒号(:)分隔的字串组成。 数组维数修饰后面跟着一个等号操作符(=)。比如:

SELECT 1 || ARRAY[2,3] AS array;

     array
---------------
[0:2]={1,2,3}
(1 row)

SELECT ARRAY[1,2] || ARRAY[[3,4]] AS array;

          array
--------------------------
[0:1][1:2]={{1,2},{3,4}}
(1 row)

这个语法也可以用于在一个数组文本中声明非缺省数组脚标。比如:

SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;

e1 | e2
----+----
  1 |  6
(1 row)

如上所述,在书写一个数组的数值的时候你要用双引号包围任意独立的数组元素。 如果元素数值可能令数组数值分析器产生歧义,那么你必须这么做。 比如,那些包含花括弧,逗号(或者任何其它的分隔字符), 双引号,反斜杠,或者前导的空白元素都必须加双引号。 要把双引号或者反斜杠放到数组元素值里,给它们加一个反斜杠前缀。 另外,你可以用反斜杠逃逸的方法保护所有那些可能被当做数组语法的字符。

你可以在左花括弧前面或者右花括弧后面写空白。你还可以在任意独立的项字串前面或者后面写空白。 所有这些情况下,这些空白都会被忽略。不过,在双引号包围的元素里面的空白,或者是元素里被两边非空白字符包围的空白,都不会被忽略。

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

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

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

    提示: ARRAY 构造器语法(参阅 Section 4.2.10)通常比数组文本语法好用些,尤其是在 SQL 命令里写数组值的时候。 在 ARRAY 里,独立的元素值的写法和数组里没有元素时的写法一样。

TOP


感谢一直以来您对我们的支持!
当前时区 GMT+8, 现在时间是 2008-8-22 11:58 京ICP证060528 号

Designed By 17DST