PostgreSQL教程(四):数据类型详解

一、数值类型:

下面是PostgreSQL所支持的数值类型的列表和简单说明:

1. 整数类型:

类型smallint、integer和bigint存储各种范围的全部是数字的数,也就是没有小数部分的数字。试图存储超出范围以外的数值将导致一个错误。常用的类型是integer,因为它提供了在范围、存储空间和性能之间的最佳平衡。一般只有在磁盘空间紧张的时候才使用smallint。而只有在integer的范围不够的时候才使用bigint,因为前者(integer)绝对快得多。

    2. 任意精度数值:

类型numeric可以存储最多1000位精度的数字并且准确地进行计算。因此非常适合用于货币金额和其它要求计算准确的数量。不过,numeric类型上的算术运算比整数类型或者浮点数类型要慢很多。
    numeric字段的最大精度和最大比例都是可以配置的。要声明一个类型为numeric的字段,你可以用下面的语法:
 

代码如下:

NUMERIC(precision,scale)

比如数字23.5141的精度为6,而刻度为4。
    在目前的PostgreSQL版本中,decimal和numeric是等效的。
    
    3. 浮点数类型:

数据类型real和double是不准确的、牺牲精度的数字类型。不准确意味着一些数值不能准确地转换成内部格式并且是以近似的形式存储的,因此存储后再把数据打印出来可能显示一些缺失。
    
   4. Serial(序号)类型:
    serial和bigserial类型不是真正的类型,只是为在表中设置唯一标识做的概念上的便利。
 

代码如下:

CREATE TABLE tablename (
        colname SERIAL
    );

等价于
 

代码如下:

CREATE SEQUENCE tablename_colname_seq;
    CREATE TABLE tablename(
        colname integer DEFAULT nextval('tablename_colname_seq') NOT NULL
    );

这样,我们就创建了一个整数字段并且把它的缺省数值安排为从一个序列发生器取值。应用了一个NOT NULL约束以确保空值不会被插入。在大多数情况下你可能还希望附加一个UNIQUE或者PRIMARY KEY约束避免意外地插入重复的数值,但这个不是自动发生的。因此,如果你希望一个序列字段有一个唯一约束或者一个主键,那么你现在必须声明,就像其它数据类型一样。
    还需要另外说明的是,一个serial类型创建的序列在其所属字段被删除时,该序列也将被自动删除,但是其它情况下是不会被删除的。因此,如果你想用同一个序列发生器同时给几个字段提供数据,那么就应该以独立对象的方式创建该序列发生器。

二、字符类型:

下面是PostgreSQL所支持的字符类型的列表和简单说明:

SQL 定义了两种基本的字符类型,varchar(n)和char(n),这里的n是一个正整数。两种类型都可以存储最多n个字符长的字串,试图存储更长的字串到这些类型的字段里会产生一个错误,除非超出长度的字符都是空白,这种情况下该字串将被截断为最大长度。如果没有长度声明,char等于char(1),而varchar则可以接受任何长度的字串。

代码如下:

MyTest=> CREATE TABLE testtable(first_col varchar(2));
    CREATE TABLE
    MyTest=> INSERT INTO testtable VALUES('333');   --插入字符串的长度,超过其字段定义的长度,因此报错。
    ERROR:  value too long for type character varying(2)
    --插入字符串中,超出字段定义长度的部分是空格,因此可以插入,但是空白符被截断。
    MyTest=> INSERT INTO testtable VALUES('33 ');  
    INSERT 0 1
    MyTest=> SELECT * FROM testtable;
     first_col
    -----------
     33
    (1 row)

这里需要注意的是,如果是将数值转换成char(n)或者varchar(n),那么超长的数值将被截断成n个字符,而不会抛出错误。

代码如下:

MyTest=> select 1234::varchar(2);
     varchar
    ---------
     12
    (1 row)

最后需要提示的是,这三种类型之间没有性能差别,只不过是在使用char类型时增加了存储尺寸。虽然在某些其它的数据库系统里,char(n)有一定的性能优势,但在PostgreSQL里没有。在大多数情况下,应该使用text或者varchar。
   
三、日期/时间类型:

下面是PostgreSQL所支持的日期/时间类型的列表和简单说明:

1. 日期/时间输入:
    任何日期或者时间的文本输入均需要由单引号包围,就象一个文本字符串一样。
    1). 日期:
    以下为合法的日期格式列表:

2). 时间:
    以下为合法的时间格式列表:

3). 时间戳:

时间戳类型的有效输入由一个日期和时间的联接组成,后面跟着一个可选的时区。因此,1999-01-08 04:05:06和1999-01-08 04:05:06 -8:00都是有效的数值。
  
   2. 示例:
    1). 在插入数据之前先查看datestyle系统变量的值:
 

代码如下:

MyTest=> show datestyle;
     DateStyle
    -----------
     ISO, YMD
    (1 row)

2). 创建包含日期、时间和时间戳类型的示例表:
 

代码如下:

MyTest=> CREATE TABLE testtable (id integer, date_col date, time_col time, timestamp_col timestamp);
    CREATE TABLE

3). 插入数据:
 

代码如下:

MyTest=> INSERT INTO testtable(id,date_col) VALUES(1, DATE'01/02/03');  --datestyle为YMD
    INSERT 0 1
    MyTest=> SELECT id, date_col FROM testtable;
     id   |  date_col
    ----+------------
      1  | 2001-02-03
    (1 row)
   
    MyTest=> set datestyle = MDY;
    SET
    MyTest=> INSERT INTO testtable(id,date_col) VALUES(2, DATE'01/02/03');  --datestyle为MDY
    INSERT 0 1
    MyTest=> SELECT id,date_col FROM testtable;
     id   |  date_col
    ----+------------
      1  | 2001-02-03
      2  | 2003-01-02

MyTest=> INSERT INTO testtable(id,time_col) VALUES(3, TIME'10:20:00');  --插入时间。
    INSERT 0 1
    MyTest=> SELECT id,time_col FROM testtable WHERE time_col IS NOT NULL;
     id   | time_col
    ----+----------
      3   | 10:20:00
    (1 row)

MyTest=> INSERT INTO testtable(id,timestamp_col) VALUES(4, DATE'01/02/03');
    INSERT 0 1
    MyTest=> INSERT INTO testtable(id,timestamp_col) VALUES(5, TIMESTAMP'01/02/03 10:20:00');
    INSERT 0 1
    MyTest=> SELECT id,timestamp_col FROM testtable WHERE timestamp_col IS NOT NULL;
     id   |    timestamp_col
    ----+---------------------
      4  | 2003-01-02 00:00:00
      5  | 2003-01-02 10:20:00
    (2 rows)

四、布尔类型:

PostgreSQL支持标准的SQL boolean数据类型。boolean只能有两个状态之一:真(True)或 假(False)。该类型占用1个字节。
    "真"值的有效文本值是:
 

代码如下:

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

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

代码如下:

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

  见如下使用方式:

代码如下:

MyTest=> CREATE TABLE testtable (a boolean, b text);
    CREATE TABLE
    MyTest=> INSERT INTO testtable VALUES(TRUE, 'sic est');
    INSERT 0 1
    MyTest=> INSERT INTO testtable VALUES(FALSE, 'non est');
    INSERT 0 1
    MyTest=> SELECT * FROM testtable;
     a  |    b
    ---+---------
     t  | sic est
     f  | non est
    (2 rows)   
    MyTest=> SELECT * FROM testtable WHERE a;
     a  |    b
    ---+---------
     t  | sic est
    (1 row)   
    MyTest=> SELECT * FROM testtable WHERE a = true;
     a  |    b
    ---+---------
     t  | sic est
    (1 row)

五、位串类型:

位串就是一串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位,那么它的右边将被截断。 见如下具体使用方式:   
 

代码如下:

MyTest=> CREATE TABLE testtable (a bit(3), b bit varying(5));
    CREATE TABLE
    MyTest=> INSERT INTO testtable VALUES (B'101', B'00');
    INSERT 0 1
    MyTest=> INSERT INTO testtable VALUES (B'10', B'101');
    ERROR:  bit string length 2 does not match type bit(3)
    MyTest=> INSERT INTO testtable VALUES (B'10'::bit(3), B'101');
    INSERT 0 1
    MyTest=> SELECT * FROM testtable;
      a  |  b
    -----+-----
     101 | 00
     100 | 101
    (2 rows)
    MyTest=> SELECT B'11'::bit(3);
     bit
    -----
     110
    (1 row)

六、数组:

1. 数组类型声明:
    1). 创建字段含有数组类型的表。
 

代码如下:

CREATE TABLE sal_emp (
        name            text,
        pay_by_quarter  integer[] --还可以定义为integer[4]或integer ARRAY[4]
    );

2). 插入数组数据:
 

代码如下:

MyTest=# INSERT INTO sal_emp VALUES ('Bill', '{11000, 12000, 13000, 14000}');
    INSERT 0 1
    MyTest=# INSERT INTO sal_emp VALUES ('Carol', ARRAY[21000, 22000, 23000, 24000]);
    INSERT 0 1
    MyTest=# SELECT * FROM sal_emp;
     name  |      pay_by_quarter
    --------+---------------------------
     Bill     | {11000,12000,13000,14000}
     Carol  | {21000,22000,23000,24000}
    (2 rows)

2. 访问数组:
    和其他语言一样,PostgreSQL中数组也是通过下标数字(写在方括弧内)的方式进行访问,只是PostgreSQL中数组元素的下标是从1开始n结束。
 

代码如下:

MyTest=# SELECT pay_by_quarter[3] FROM sal_emp;
     pay_by_quarter
    ----------------
              13000
              23000
    (2 rows)
    MyTest=# SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];
     name
    ------
     Bill
     Carol
    (2 rows)

PostgreSQL中还提供了访问数组范围的功能,即ARRAY[脚标下界:脚标上界]。
 

代码如下:

MyTest=# SELECT name,pay_by_quarter[1:3] FROM sal_emp;
     name  |   pay_by_quarter
    --------+---------------------
     Bill     | {11000,12000,13000}
     Carol  | {21000,22000,23000}
    (2 rows)

3. 修改数组:
    1). 代替全部数组值:
 

代码如下:

--UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] WHERE name = 'Carol'; 也可以。
    MyTest=# UPDATE sal_emp SET pay_by_quarter = '{31000,32000,33000,34000}' WHERE name = 'Carol';
    UPDATE 1
    MyTest=# SELECT * FROM sal_emp;
     name  |      pay_by_quarter
    --------+---------------------------
     Bill     | {11000,12000,13000,14000}
     Carol  | {31000,32000,33000,34000}
    (2 rows)

2). 更新数组中某一元素:
 

代码如下:

MyTest=# UPDATE sal_emp SET pay_by_quarter[4] = 15000 WHERE name = 'Bill';
    UPDATE 1
    MyTest=# SELECT * FROM sal_emp;
     name  |      pay_by_quarter
    --------+---------------------------
     Carol  | {31000,32000,33000,34000}
     Bill     | {11000,12000,13000,15000}
    (2 rows)

3). 更新数组某一范围的元素:
 

代码如下:

MyTest=# UPDATE sal_emp SET pay_by_quarter[1:2] = '{37000,37000}' WHERE name = 'Carol';
    UPDATE 1
    MyTest=# SELECT * FROM sal_emp;
     name  |      pay_by_quarter
    --------+---------------------------
     Bill     | {11000,12000,13000,15000}
     Carol  | {37000,37000,33000,34000}
    (2 rows)

4). 直接赋值扩大数组:
 

代码如下:

MyTest=# UPDATE sal_emp SET pay_by_quarter[5] = 45000 WHERE name = 'Bill';
    UPDATE 1
    MyTest=# SELECT * FROM sal_emp;
     name  |         pay_by_quarter
    --------+---------------------------------
     Carol  | {37000,37000,33000,34000}
     Bill     | {11000,12000,13000,15000,45000}
    (2 rows)

4. 在数组中检索:
    1). 最简单直接的方法:
 

代码如下:

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;

2). 更加有效的方法:
 

代码如下:

SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter); --数组元素中有任何一个等于10000,where条件将成立。
    SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter); --只有当数组中所有的元素都等于10000时,where条件才成立。

七、复合类型:

PostgreSQL中复合类型有些类似于C语言中的结构体,也可以被视为Oracle中的记录类型,但是还是感觉复合类型这个命名比较贴切。它实际上只是一个字段名和它们的数据类型的列表。PostgreSQL允许像简单数据类型那样使用复合类型。比如,表字段可以声明为一个复合类型。

1. 声明复合类型:

下面是两个简单的声明示例:
 

代码如下:

CREATE TYPE complex AS (
        r double,
        i double
    );  
    CREATE TYPE inventory_item AS (
        name           text,
        supplier_id   integer,
        price            numeric
    );

和声明一个数据表相比,声明类型时需要加AS关键字,同时在声明TYPE时不能定义任何约束。下面我们看一下如何在表中指定复合类型的字段,如:
 

代码如下:

CREATE TABLE on_hand (
        item      inventory_item,
        count    integer
    );

最后需要指出的是,在创建表的时候,PostgreSQL也会自动创建一个与该表对应的复合类型,名字与表字相同,即表示该表的复合类型。
   
    2. 复合类型值输入:
    我们可以使用文本常量的方式表示复合类型值,即在圆括号里包围字段值并且用逗号分隔它们。你也可以将任何字段值用双引号括起,如果值本身包含逗号或者圆括号,那么就用双引号括起,对于上面的inventory_item复合类型的输入如下:
 

代码如下:

'("fuzzy dice",42,1.99)'

如果希望类型中的某个字段为NULL,只需在其对应的位置不予输入即可,如下面的输入中price字段的值为NULL,
 

代码如下:

'("fuzzy dice",42,)'

如果只是需要一个空字串,而非NULL,写一对双引号,如:
 

代码如下:

'("",42,)'

在更多的场合中PostgreSQL推荐使用ROW表达式来构建复合类型值,使用该种方式相对简单,无需考虑更多标识字符问题,如:
 

代码如下:

ROW('fuzzy dice', 42, 1.99)
    ROW('', 42, NULL)

注:对于ROW表达式,如果里面的字段数量超过1个,那么关键字ROW就可以省略,因此以上形式可以简化为:
 

代码如下:

('fuzzy dice', 42, 1.99)
    ('', 42, NULL)

3. 访问复合类型:
    访问复合类型中的字段和访问数据表中的字段在形式上极为相似,只是为了对二者加以区分,PostgreSQL设定在访问复合类型中的字段时,类型部分需要用圆括号括起,以避免混淆,如:
 

代码如下:

SELECT (item).name FROM on_hand WHERE (item).price > 9.99;

如果在查询中也需要用到表名,那么表名和类型名都需要被圆括号括起,如:
 

代码如下:

SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;

4. 修改复合类型:
    见如下几个示例:
 

代码如下:

--直接插入复合类型的数据,这里是通过ROW表达式来完成的。
    INSERT INTO on_hand(item) VALUES(ROW("fuzzy dice",42,1.99));
    --在更新操作中,也是可以通过ROW表达式来完成。
    UPDATE on_hand SET item = ROW("fuzzy dice",42,1.99) WHERE count = 0;
    --在更新复合类型中的一个字段时,我们不能在SET后面出现的字段名周围加圆括号,
    --但是在等号右边的表达式里引用同一个字段时却需要圆括号。
    UPDATE on_hand SET item.price = (item).price + 1 WHERE count = 0;
    --可以在插入中,直接插入复合类型中字段。
    INSERT INTO on_hand (item.supplier_id, item.price) VALUES(100, 2.2);

该篇博客是对PostgreSQL官方文档中“数据类型”章节的简单归纳,这里之所以用一篇独立的博客来专门介绍,不仅是为了系统学习,也便于今后需要时的快速查阅。

(0)

相关推荐

  • PostgreSQL教程(七):函数和操作符详解(3)

    九.序列操作函数: 序列对象(也叫序列生成器)都是用CREATE SEQUENCE创建的特殊的单行表.一个序列对象通常用于为行或者表生成唯一的标识符.下面序列函数,为我们从序列对象中获取最新的序列值提供了简单和并发读取安全的方法. 函数 返回类型 描述 nextval(regclass) bigint 递增序列对象到它的下一个数值并且返回该值.这个动作是自动完成的.即使多个会话并发运行nextval,每个进程也会安全地收到一个唯一的序列值. currval(regclass) bigint 在当

  • PostgreSQL教程(五):函数和操作符详解(1)

    一.逻辑操作符: 常用的逻辑操作符有:AND.OR和NOT.其语义与其它编程语言中的逻辑操作符完全相同. 二.比较操作符: 下面是PostgreSQL中提供的比较操作符列表: 比较操作符可以用于所有可以比较的数据类型.所有比较操作符都是双目操作符,且返回boolean类型.除了比较操作符以外,我们还可以使用BETWEEN语句,如:     a BETWEEN x AND y 等效于 a >= x AND a <= y        a NOT BETWEEN x AND y 等效于 a <

  • PostgreSQL教程(六):函数和操作符详解(2)

    六.模式匹配: PostgreSQL中提供了三种实现模式匹配的方法:SQL LIKE操作符,更近一些的SIMILAR TO操作符,和POSIX-风格正则表达式.     1. LIKE:   复制代码 代码如下: string LIKE pattern [ ESCAPE escape-character ]     string NOT LIKE pattern [ ESCAPE escape-character ] 每个pattern定义一个字串的集合.如果该string包含在pattern代

  • PostgreSQL教程(一):数据表详解

    一.表的定义: 对于任何一种关系型数据库而言,表都是数据存储的最核心.最基础的对象单元.现在就让我们从这里起步吧.     1. 创建表:   复制代码 代码如下: CREATE TABLE products (         product_no integer,         name text,         price numeric     );     2. 删除表:   复制代码 代码如下: DROP TABLE products;     3. 创建带有缺省值的表:   复

  • PostgreSQL教程(十三):数据库管理详解

    一.概述: 数据库可以被看成是SQL对象(数据库对象)的命名集合,通常而言,每个数据库对象(表.函数等)只属于一个数据库.不过对于部分系统表而言,如pg_database,是属于整个集群的.更准确地说,数据库是模式的集合,而模式包含表.函数等SQL对象.因此完整的对象层次应该是这样的:服务器.数据库.模式.表或其他类型的对象. 在与数据库服务器建立连接时,该连接只能与一个数据库形成关联,不允许在一个会话中进行多个数据库的访问.如以postgres用户登录,该用户可以访问的缺省数据库为postgr

  • PostgreSQL教程(十一):服务器配置

    一.服务器进程的启动和关闭: 下面是pg_ctl命令的使用方法和常用选项,需要指出的是,该命令是postgres命令的封装体,因此在使用上比直接使用postgres更加方便. 复制代码 代码如下: pg_ctl init[db] [-D DATADIR] [-s] [-o "OPTIONS"]     pg_ctl start     [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o "OPTIONS"]    

  • PostgreSQL教程(十四):数据库维护

    一.恢复磁盘空间: 在PostgreSQL中,使用delete和update语句删除或更新的数据行并没有被实际删除,而只是在旧版本数据行的物理地址上将该行的状态置为已删除或已过期.因此当数据表中的数据变化极为频繁时,那么在一段时间之后该表所占用的空间将会变得很大,然而数据量却可能变化不大.要解决该问题,需要定期对数据变化频繁的数据表执行VACUUM操作. VACUUM命令存在两种形式,VACUUM和VACUUM FULL,它们之间的区别见如下表格:   无VACUUM VACUUM VACUUM

  • PostgreSQL教程(八):索引详解

    一.索引的类型: PostgreSQL提供了多 种索引类型:B-Tree.Hash.GiST和GIN,由于它们使用了不同的算法,因此每种索引类型都有其适合的查询类型,缺省时,CREATE INDEX命令将创建B-Tree索引.         1. B-Tree:   复制代码 代码如下: CREATE TABLE test1 (         id integer,         content varchar     );     CREATE INDEX test1_id_index

  • PostgreSQL教程(十二):角色和权限管理介绍

    PostgreSQL是通过角色来管理数据库访问权限的,我们可以将一个角色看成是一个数据库用户,或者一组数据库用户.角色可以拥有数据库对象,如表.索引,也可以把这些对象上的权限赋予其它角色,以控制哪些用户对哪些对象拥有哪些权限.     一.数据库角色: 1. 创建角色:   复制代码 代码如下: CREATE ROLE role_name; 2. 删除角色:   复制代码 代码如下: DROP ROLE role_name; 3. 查询角色: 检查系统表pg_role,如:   复制代码 代码如

  • PostgreSQL教程(十):性能提升技巧

    一.使用EXPLAIN: PostgreSQL为每个查询都生成一个查询规划,因为选择正确的查询路径对性能的影响是极为关键的.PostgreSQL本身已经包含了一个规划器用于寻找最优规划,我们可以通过使用EXPLAIN命令来查看规划器为每个查询生成的查询规划.     PostgreSQL中生成的查询规划是由1到n个规划节点构成的规划树,其中最底层的节点为表扫描节点,用于从数据表中返回检索出的数据行.然而,不同的扫描节点类型代表着不同的表访问模式,如:顺序扫描.索引扫描,以及位图索引扫描等.如果查

  • PostgreSQL教程(二):模式Schema详解

    一个数据库包含一个或多个命名的模式,模式又包含表.模式还包含其它命名的对象,包括数据类型.函数,以及操作符.同一个对象名可以在不同的模式里使用而不会导致冲突: 比如,schema1和myschema都可以包含叫做mytable的表.和数据库不同,模式不是严格分离的:一个用户可以访问他所连接的数据库中的任意模式中的对象,只要他有权限. 我们需要模式有以下几个主要原因: 1). 允许多个用户使用一个数据库而不会干扰其它用户.     2). 把数据库对象组织成逻辑组,让它们更便于管理.     3)

  • PostgreSQL教程(九):事物隔离介绍

    在SQL的标准中事物隔离级别分为以下四种:     1. 读未提交(Read uncommitted)     2. 读已提交(Read committed)     3. 可重复读(Repeatable read)     4. 可串行化(Serializable)     然而PostgreSQL在9.1之前的版本中只是实现了其中两种,即读已提交和可串行化,如果在实际应用中选择了另外两种,那么PostgreSQL将会自动向更严格的隔离级别调整.在PostgreSQL v9.1的版本中提供了三

  • PostgreSQL教程(三):表的继承和分区表详解

    一.表的继承: 这个概念对于很多已经熟悉其他数据库编程的开发人员而言会多少有些陌生,然而它的实现方式和设计原理却是简单易懂,现在就让我们从一个简单的例子开始吧.     1. 第一个继承表:   复制代码 代码如下: CREATE TABLE cities (   --父表         name        text,         population float,         altitude     int     );     CREATE TABLE capitals (

随机推荐