8.1. 数字类型

数字类型由2、4或8字节的整数以及4或8字节的浮点数和可选精度小数组成。表 8-2列出了所有可用类型。

表 8-2. 数字类型
名字 存储尺寸 描述 范围
smallint 2字节 小范围整数 -32768 to +32767
integer 4字节 整数的典型选择 -2147483648 to +2147483647
bigint 8字节 大范围整数 -9223372036854775808 to +9223372036854775807
decimal 可变 用户指定精度,精确 最高小数点前131072位,以及小数点后16383位
numeric 可变 用户指定精度,精确 最高小数点前131072位,以及小数点后16383位
real 4字节 可变精度,不精确 6位十进制精度
double precision 8字节 可变精度,不精确 15位十进制精度
smallserial 2字节 自动增加的小整数 1到32767
serial 4字节 自动增加的整数 1到2147483647
bigserial 8字节 自动增长的大整数 1到9223372036854775807

数字类型常量的语法在 4.1. 词法结构 第 2 节里描述。数字类型有一整套对应的数学操作符和函数。相关信息请参考 第九章:函数和操作符 。下面的几节详细描述这些类型。

8.1.1. 整数类型

类型smallint、integer和bigint存储各种范围的全部是数字的数,也就是没有小数部分的数字。试图存储超出范围以外的值将导致一个错误。

常用的类型是integer,因为它提供了在范围、存储空间和性能之间的最佳平衡。一般只有在磁盘空间紧张的时候才使用 smallint类型。而只有在integer的范围不够的时候才使用bigint。

SQL只声明了整数类型integer(或int)、smallint和bigint。类型int2、int4和int8都是扩展,也在许多其它SQL数据库系统中使用。

8.1.2. 任意精度数字

类型numeric可以存储非常多位的数字。我们特别建议将它用于货币金额和其它要求计算准确的数量。numeric值的计算产生准确结果,比如加法,减法,乘法。不过,numeric类型上的算术运算比整数类型或者下一节描述的浮点数类型要慢很多。

在随后的内容里,我们使用了下述术语:一个numeric的比例 是到小数部分的位数,numeric的精度 是整个数字里全部位的数目,也就是小数点两边的位数目。因此数字 23.5141 的精度为6而比例为4。你可以认为整数的比例为零。

  • numeric列的最大精度和最大比例都是可以配置的。要声明一个类型为numeric的列,你可以用下面的语法:
    NUMERIC(precision, scale)
    
  • 精度必须为正数,比例可以为零或者正数。另外:
    NUMERIC(precision)
    
  • 选择比例为 0 。如果使用
    NUMERIC
    

创建一个列时不使用精度或比例,则该列可以存储任何精度和比例的数字值,并且值的范围最多可以到实现精度的上限。一个这种列将不会把输入值转化成任何特定的比例,而带有比例声明的numeric列将把输入值转化为该比例(SQL标准要求缺省的比例是 0,即转化成整数精度。我们觉得这样做有点没用。如果你关心移植性,那你最好总是显式声明精度和比例)。

注意: 显式指定类型精度时的最大允许精度为 1000,没有指定精度的NUMERIC受到表 8-2中描述的限制所控制。

如果一个要存储的值的比例比列声明的比例高,那么系统将尝试圆整(四舍五入)该值到指定的分数位数。 然后,如果小数点左边的位数超过了声明的精度减去声明的比例,那么抛出一个错误。

数字值在物理上是以不带任何前导或者后缀零的形式存储。 因此,列上声明的精度和比例都是最大值,而不是固定分配的 (在这个方面,numeric类型更类似于varchar(n), 而不像char(n))。 实际存储要求是每四个十进制位组用两个字节, plus three to eight bytes overhead.

除了普通的数字值之外,numeric类型允许特殊值NaN, 表示**"不是一个数字"**。任何在 NaN上面的操作都生成另外一个NaN。 如果在 SQL 命令里把这些值当作一个常量写,你必须在其周围放上单引号,例如UPDATE table SET x = 'NaN'。在输入时,字串NaN被识别为大小写无关。

注意: 在"不是一个数字"概念的大部分实现中,NaN被认为不等于任何其他数字值(包括NaN)。为了允许numeric值可以被排序和使用基于树的索引,PostgreSQL把NaN值视为相等,并且比所有非NaN值都要大。

类型decimal和numeric是等效的。两种类型都是SQL标准的一部分。

8.1.3. 浮点类型

数据类型real和double precision是不准确的、变精度的数字类型。实际上,这些类型是IEEE标准 754 二进制浮点算术(分别对应单精度和双精度)的一般实现, 一直到下层处理器、操作系统和编译器对它的支持。

不准确意味着一些值不能准确地转换成内部格式并且是以近似的形式存储的,因此存储和检索一个值可能出现一些缺失。 处理这些错误以及这些错误是如何在计算中传播的主题属于数学和计算机科学的一个完整的分支, 我们不会在这里进一步讨论它,这里的讨论仅限于如下几点:

  • 如果你要求准确的存储和计算(例如计算货币金额),应使用numeric类型。
  • 如果你想用这些类型做任何重要的复杂计算,尤其是那些你对范围情况(无穷、下溢)严重依赖的事情,那你应该仔细评诂你的实现。
  • 用两个浮点数值进行等值比较不可能总是按照期望地进行。

在大部分平台上,real类型的范围是至少 -1E+37 到 +1E+37,精度至少是 6 位小数。double precision类型通常有 -1E+308 到 +1E+308 的范围,精度是至少 15 位数字。太大或者太小的值都会导致错误。 如果输入数字的精度太高,那么可能发生园整。太接近零的数字,如果无法与零值的表现形式相区分就会产生下溢错误。

注意: extra_float_digits设置控制当一个浮点值被转换为文本输出时要包括的额外有效数字的数目。其默认值为0,在每一个PostgreSQL支持的平台上输出都相同。增加该设置将产生能更精确表示存储值的输出,但是可能无法移植。

  • 除了普通的数字值之外,浮点类型还有几个特殊值:
    Infinity
    - Infinity
    NaN
    

这些值分别表示 IEEE 754 特殊值"正无穷大""负无穷大"以及"不是一个数字"(在不遵循 IEEE 754 浮点算术的机器上,这些值的含义可能不是预期的)。如果在 SQL 命令里把这些数值当作常量写,你必须在它们周围放上单引号,例如UPDATE table SET x = 'Infinity'。 在输入时,这些串是以大小写无关的方式识别的。

注意: IEEE754指定NaN不应该与任何其他浮点值(包括NaN)相等。为了允许浮点值被排序或者在基于树的索引中使用,PostgreSQL将NaN值视为相等,并且比所有非NaN值要更大。

**PostgreSQL**还支持 SQL 标准表示法float和float(p)用于声明非精确的数字类型。在这里,p指定以二进制位表示的最低可接受精度。 在选取real类型的时候,PostgreSQL接受float(1)到float(24),在选取double precision的时候,接受float(25)到float(53)。在允许范围之外的p值将导致一个错误。没有指定精度的float将被当作是double precision。

注意: 认为real和double precision分别有 24 和 53 个二进制位的假设对 IEEE 标准的浮点实现来说是正确的。在非 IEEE 平台上,这个数值可能略有偏差,但是为了简化,我们在所有平台上都用了同样的p值范围。

8.1.4. 序数类型

smallserial、serial和bigserial类型不是真正的类型,它们只是为了创建唯一标识符列而存在的方便符号(类似其它一些数据库中支持的AUTO_INCREMENT属性)。

  • 在目前的实现中,下面一个语句:
    CREATE TABLE tablename (
        colname SERIAL
    );
    
  • 等价于以下语句:
    CREATE SEQUENCE tablename_colname_seq;
    CREATE TABLE tablename (
        colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
    );
    ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
    

因此,我们就创建了一个整数列并且把它的缺省值安排为从一个序列发生器取值。应用了一个NOT NULL约束以确保空值不会被插入(在大多数情况下你可能还希望附加一个UNIQUE或者PRIMARY KEY约束避免意外地插入重复的值,但这个不是自动发生的)。最后,该序列被标记为"属于"该列,这样当列或表被删除时该序列也会被删除。

注意: 因为smallserial、serial和bigserial是用序列实现的,所以即使没有删除过行,在出现在列中的序列值可能有“空洞”或者间隙。如果一个从序列中分配的值被用在一行中,即使该行最终没有被成功地插入到表中,该值也被“用掉”了。例如,当插入事务回滚时就会发生这种情况。更多信息参见第 9.16 节中的nextval()。

要使用serial列插入序列的下一个数值到表中, 请指定serial列应该被赋予其缺省值。我们可以通过在INSERT语句中把该列排除在列列表之外来实现,也可以通过使用DEFAULT关键字来实现。

类型名serial和serial4是等效的: 两个都创建integer列。类型名bigserial和serial8也一样,只不过它们创建一个 bigint列。如果你预计在表的生存期中使用的标识符数目超过 \( 2^{31} \) 个,那么你应该使用bigserial。类型名smallserial和serial2也以相同方式工作,只不过它们创建一个smallint列。

为一个serial列创建的序列在所属的列被删除的时候自动删除。你可以在不删除列的情况下删除序列,但是这会强制删除该列的默认值表达式。