PostgreSQL - 数组(Array)

·528 字·3 分钟
PostgreSQL database nosql
n3xtchen
作者
n3xtchen
Sharing Funny Tech With You

PostgreSQL 可以通过一种数据结构在独立字段中存储非原子的值。这个数据结构就是我们今天要谈的数组(Array),它本身不是一种数据类型,而是任何数据类型的一种拓展。

创建一个数组字段 #

CREATE TABLEALTER 语句中需要声明成数组的字段类型后面追加上方括号,这样就可以创建一个数组字段。

现在来看个例子,声明一个一维数组:

single_array type[]

可以将多个方括号追加在类型的后面,就可以声明一个多维数组,可以在数组中存储数组:

multi_array type[][]

理论上说,可以在方括号中插入一个整数 n 来声明一个定长(fixed-length)的数组。但是在 PostgreSQL 7.1.x 之后,这个限制非强制的,实际上定长数组和非定长数组之间没有实际的不同。

接下来是一个创建包含数组的表:

pigdb> CREATE TABLE faviroute_book (
employee_id INTEGER,
books TEXT[]
);
CREATE TABLE

这个例子中,books 字段存储任何数量的书名数组。创建多维数据的方法也是类似的,唯一的区别就是有另一对方括号要紧跟在第一对的后面,就像前面的例子那样。

pigdb> CREATE TABLE favorite_authors (
employee_id INTEGER,
author_and_titles TEXT[][]
);
CREATE TABLE

给数组追加数据 #

为了在一个字段插入多个值,PostgreSQL 引入了一个特殊的语法。这个语法允许你描述一个数组常量。这个数组常量有话括号,双引号和逗号组成,外层由单引号包裹着。双引号只有在字符数组的时候,需要用到,通用的模式:

'{"text1",[...]}'	 -- 字符数组
'{ numeric, [...]}'  -- 数字数组

这个语法模式演示如何使用字符和数字数组,但是每个数组必须定义单一数据类型(可以是任何的数据类型,boolean,date 或者是 time)。通常情况下,你使用单引号来描述非数组上下文的值,双引号用来数组中的值。

现在,我们插入数据:

pigdb> INSERT INTO faviroute_book VALUES (102, E'{"The Hichhiker \'s GUIDE to he Galaxy"}');	-- 插入一个值的数组,注意这边的单引号需要转移,E - escape, 后续会单独讲解这块
INSERT 0 1
pigdb> INSERT INTO faviroute_book VALUES (103, '{"The Hobbit", "Kitten, Squared"}');	-- 插入多个值的数组
INSERT 0 1  
pigdb> SELECT * FROM faviroute_book;
+---------------+------------------------------------------+
|   employee_id | books                                    |
|---------------+------------------------------------------|
|           102 | [u"The Hichhiker 's GUIDE to he Galaxy"] |
|           103 | [u'The Hobbit', u'Kitten, Squared']      |
+---------------+------------------------------------------+

在插入单个值的时候,花括号还是需要的,这是因为数组常量本身也需要被当作字符串来解析,随后基于上下文解析成真正的数组。

多维数组的插入也是类似,看看例子:

pigdb> INSERT INTO favorite_authors VALUES (102,
	'{ {"J.R.R. Tokeien", "The Silmarillion"}, {"Charless Dickness", "Great Expectations"} }'
);
INSERT 0 1
pigdb> SELECT * FROM favorite_authors;
+---------------+-------------------------------------------------------------------------------------------+
|   employee_id | author_and_titles                                                                         |
|---------------+-------------------------------------------------------------------------------------------|
|           102 | [[u'J.R.R. Tokeien', u'The Silmarillion'], [u'Charless Dickness', u'Great Expectations']] |
+---------------+-------------------------------------------------------------------------------------------+

查询数组 #

之前已经演示查询数组字段,它会返回整个数组。但是数组的最大用处实际是依赖于他的下标操作。

数组下标(Subscripts) #

下标的语法由方括号包围着整形数值;这个数值描述的是从左开始的位置距离。

不像大部分编程语言(比如 C),PostgreSQL 的下标是从 1 开始的,而不是 0;现在看个例子

pigdb> SELECT books[1] FROM faviroute_book;
+-------------------------------------+
| books                               |
|-------------------------------------|
| The Hichhiker 's GUIDE to he Galaxy |
| The Hobbit                          |
+-------------------------------------+
SELECT 2

注意到了吧,查询返回的值不包含话括号的双引号;这是因为单个文本值只需要作为文本常量返回,而不是一个数组;在看一条:

pigdb> SELECT books[2] FROM faviroute_book;
+-----------------+
| books           |
|-----------------|
|                 |
| Kitten, Squared |
+-----------------+

如果筛选的的结果值被存在会返回空值。你可以使用 IS NOT NULL 的语法来过滤它:

pigdb> SELECT books[2] FROM faviroute_book WHERE books[2] IS NOT NULL;
+-----------------+
| books           |
|-----------------|
| Kitten, Squared |
+-----------------+

现在,我们来操作下多维数组:

pigdb> SELECT 
	author_and_titles[1][1] AS author, 
	author_and_titles[1][2] AS title 
FROM favorite_authors;
+----------------+------------------+
| author         | title            |
|----------------+------------------|
| J.R.R. Tokeien | The Silmarillion |
+----------------+------------------+
SELECT 1

数组切片(Slices) #

PostgreSQL 同样支持数组的切片操作;原理和下标操作类似,只是它返回制定范围段的值。切片的语法由一对整形数字,被冒号隔开,被方括号包围;例如 [2:5] 返回的是,数组的第二,第三,第四和第五的值,以数组形式返回:

pigdb> INSERT INTO faviroute_book VALUES (104, '{"The Hobbit", "Kitten, Squared", "Practical PostgreSQL"}');	-- 为了掩饰效果,插入一条数组
INSERT 0 1
pigdb> SELECT * FROM faviroute_book;
+---------------+--------------------------------------------------------------+
|   employee_id | books                                                        |
|---------------+--------------------------------------------------------------|
|           102 | [u"The Hichhiker 's GUIDE to he Galaxy"]                     |
|           103 | [u'The Hobbit', u'Kitten, Squared']                          |
|           104 | [u'The Hobbit', u'Kitten, Squared', u'Practical PostgreSQL'] |
+---------------+--------------------------------------------------------------+
SELECT 3
pigdb> SELECT books[1:2] FROM faviroute_book;
+------------------------------------------+
| books                                    |
|------------------------------------------|
| [u"The Hichhiker 's GUIDE to he Galaxy"] |
| [u'The Hobbit', u'Kitten, Squared']      |
| [u'The Hobbit', u'Kitten, Squared']      |
+------------------------------------------+
SELECT 3

而多维数组在切片方面有点不可预期,因此不推荐在多维数组使用切片操作。

数组维度(Dimensions) #

有些时候,我们需要知道数组中存储值的数量,你可以使用 array_dims() 函数;它接受一个数组参数,返回的字符串的格式和切片语法相同,描述有点拗口,不多说,看看例子就知道了:

pigdb> SELECT array_dims(books) FROM faviroute_book;
+--------------+
| array_dims   |
|--------------|
| [1:1]        |
| [1:2]        |
| [1:3]        |
+--------------+
SELECT 3

更新数组 #

数组更新的方式有三种:

  • 整体变更:修改整个字段
  • 切片变更:修改某个范围的值
  • 元素变更:修改单个元素的值

PostgreSQL 对数组的更新操作没有限制,我们通过几个简单的例子就可以一窥究竟了;

pigdb> UPDATE faviroute_book SET 
	books=E'{"The Hichhiker\'s GUIDE to he Galaxy"}' 
WHERE employee_id = 102;	-- 整体变更
UPDATE 1
pigdb> UPDATE faviroute_book SET 
	books[2:3]='{"Kitten, Squared", "Practical PostgreSQL-2014"}' 
WHERE employee_id = 104;	-- 切片变更
UPDATE 1
pigdb> UPDATE faviroute_book SET 
	books[1]=E'{"There and Back Again: AHobbit\'s Holiday"}' 
WHERE employee_id = 103;	-- 元素变更
UPDATE 1
pigdb> SELECT * FROM faviroute_book;
+---------------+-----------------------------------------------------------------------+
|   employee_id | books                                                                 |
|---------------+-----------------------------------------------------------------------|
|           102 | [u"The Hichhiker's GUIDE to he Galaxy"]                               |
|           104 | [u'The Hobbit', u'Kitten, Squared', u'Practical PostgreSQL-2014']     |
|           103 | [u'{"There and Back Again: AHobbit\'s Holiday"}', u'Kitten, Squared'] |
+---------------+-----------------------------------------------------------------------+
SELECT 3

字符转义 http://stackoverflow.com/questions/19812597/postgresql-string-escaping-settings http://blog.163.com/digoal@126/blog/static/163877040201342185210972/