9.3 9.4 9.5 9.6 10 11 12 13 14 Current(15)
阿里云PostgreSQL 问题报告 纠错本页面

8.1. 数字类型

8.1.1. 整数类型
8.1.2. 任意精度数字
8.1.3. 浮点类型
8.1.4. 序数类型

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

表 8.2. 数字类型

名字存储尺寸描述范围
smallint2字节小范围整数-32768 to +32767
integer4字节整数的典型选择-2147483648 to +2147483647
bigint8字节大范围整数-9223372036854775808 to +9223372036854775807
decimal可变用户指定精度,精确最高小数点前131072位,以及小数点后16383位
numeric可变用户指定精度,精确最高小数点前131072位,以及小数点后16383位
real4字节可变精度,不精确6位十进制精度
double precision8字节可变精度,不精确15位十进制精度
smallserial2字节自动增加的小整数1到32767
serial4字节自动增加的整数1到2147483647
bigserial8字节自动增长的大整数1到9223372036854775807

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

8.1.1. 整数类型

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

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

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

8.1.2. 任意精度数字

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

在随后的内容里,我们使用了下述术语:一个numericprecision(精度)是整个数中有效位的总数,也就是小数点两边的位数。numericscale(小数位数)是小数部分的数字位数,也就是小数点右边的部分。因此数字 23.5141 的精度为6而小数位数为4。可以认为整数的小数位数为零。

numeric的最大精度和最大标度都可以配置。要声明numeric类型的列,请使用以下语法:

NUMERIC(precision, scale)

精度必须是正数,而标度可以是正数或负数(见下文)。另外:

NUMERIC(precision)

选择标度为0。指定:

NUMERIC

没有任何精度或标度会创建一个无约束的数值列,其中可以存储任意长度的数值,直到实现限制。这种列不会强制输入值到任何特定的标度,而带有声明标度的numeric列会强制输入值到该标度。 (SQL标准要求默认标度为0,即强制到整数精度。我们觉得这有点无用。如果你关心可移植性,总是明确指定精度和标度。)

注意

可以在numeric类型声明中明确指定的最大精度为1000。一个无约束的numeric列受到在表 8.2中描述的限制。

如果要存储的值的范围大于列的声明范围,则系统将将该值四舍五入到指定的小数位数。 然后,如果小数点左边的数字位数超过声明精度减去声明范围,则会引发错误。 例如,声明为

NUMERIC(3, 1)

的列将将值四舍五入到1位小数,并且可以存储在-99.9和99.9之间的值,包括边界值。

PostgreSQL 15开始,允许声明带有负标度的numeric列。 然后值将四舍五入到小数点左侧。精度仍表示最大非四舍五入数字的数量。 因此,声明为

NUMERIC(2, -3)

的列将值四舍五入到最接近的千位,并可以存储在-99000和99000之间的值,包括这两个值。 也允许声明比声明的精度更大的标度。这样的列只能容纳小数值,并且需要小数点右侧的零位数至少等于声明的标度减去声明的精度。 例如,声明为

NUMERIC(3, 5)

的列将值四舍五入到5位小数,并可以存储在-0.00999和0.00999之间的值,包括这两个值。

注意

PostgreSQL允许在numeric类型声明中的比例为-1000到1000的任意值。 然而,SQL标准要求比例在0到precision的范围内。 使用超出该范围的比例可能无法在其他数据库系统中移植。

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

除了普通的numeric值以外,numeric类型还有如下几个特殊值:


Infinity
-Infinity
NaN

这些是根据IEEE 754标准改编的,分别表示infinitynegative infinity,和not-a-number。在SQL命令中将这些值作为常量写入时,必须在其周围加引号,例如UPDATE table SET x = '-Infinity'。输入时,以不区分大小写的方式识别这些字符串。无穷大值也可以拼写为inf-inf

无穷大值的行为符合数学期望。例如,Infinity加上任何有限值等于Infinity,就像Infinity加上Infinite一样;但是Infinity减去Infinity 得到NaN (不是一个数字),因为它没有良好定义的解释。请注意,无穷大只能存储在无约束的numeric 列中,因为它名义上超过了任何有限精度限制。

NaN(不是数字)值用于表示未定义的计算结果。 通常,任何具有 NaN输入的操作都会产生另一个 NaN。 唯一的例外是当操作的其他输入使得如果将NaN替换为任何有限或无限数值时将获得相同的输出,那么,该输出值也适用于 NaN。 (这个原理的一个例子是,NaN的零次方会得到 1。)

注意

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

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

在对值进行圆整时,numeric类型会圆到远离零的整数,而(在大部分机器上)realdouble precision类型会圆到最近的偶数上。例如:

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
  x   | num_round | dbl_round
------+-----------+-----------
 -3.5 |        -4 |        -4
 -2.5 |        -3 |        -2
 -1.5 |        -2 |        -2
 -0.5 |        -1 |        -0
  0.5 |         1 |         0
  1.5 |         2 |         2
  2.5 |         3 |         2
  3.5 |         4 |         4
(8 rows)

8.1.3. 浮点类型

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

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

  • 如果你要求准确的存储和计算(例如计算货币金额),应使用numeric类型。

  • 如果你想用这些类型做任何重要的复杂计算,尤其是那些你对范围情况(无穷、下溢)严重依赖的事情,那你应该仔细评诂你的实现。

  • 用两个浮点数值进行等值比较不可能总是按照期望地进行。

在所有当前支持的平台上,real类型的范围是 1E-37 to 1E+37 ,精度至少是 6 位小数。 double precision类型的范围是 1E-307 to 1E+308 ,精度至少是 15 位数字。 太大或者太小的值都会导致错误。 如果输入数字的精度太高,那么可能发生四舍五入。 太接近零的数字,如果不能体现出与零的区别就会导致下溢错误。

默认情况下,浮点值以其最短精确的十进制表示的文本形式输出;所产生的十进制值与相同二进制精度的任何其他的值表示相比,更接近于真实存储的二进制值。 (但是,当前输出值永远不会精确地处于两个可表示的值之间,以免输入程序不能正确遵守舍近取整法则。) 对于float8值,此值最多使用 17 个有效十进制数字,对于float4值,最多使用9个数字。

注意

生成这种最短精确的输出格式比历史的四舍五入的格式要快得多。

为了与PostgreSQL的较旧版本生成的输出兼容,并允许降低输出精度,可以使用extra_float_digits参数选择四舍五入的十进制输出。 将值设置为0将恢复以前的默认值,即将值四舍五入为6(对于float4)或15(对于float8)个有效的十进制数字。 设置负值会进一步减少位数。 例如-2会将输出分别舍入到4或13位数字。

设置extra_float_digits位任何大于 0 的值将选择最短精确格式。

注意

需要更精确值的应用需要设置extra_float_digits为3以获取更精确值。 为了版本之间的最大兼容性,他们可以继续这样做。

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


Infinity
-Infinity
NaN

这些分别代表 IEEE 754 特殊值infinitynegative infinity以及not-a-number, 如果在 SQL 命令里把这些数值当作常量写,你必须在它们周围放上单引号,例如UPDATE table SET x = '-Infinity'。 在输入时,这些字符串是以大小写不敏感的方式识别的。 无穷大值也可以拼写为inf-inf

注意

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

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

8.1.4. 序数类型

注意

这一节描述了PostgreSQL特有的创建一个自增列的方法。另一种方法是使用SQL标准的标识列特性,它在CREATE TABLE中描述。

smallserialserialbigserial类型不是真正的类型,它们只是为了创建唯一标识符列而存在的方便符号(类似其它一些数据库中支持的AUTO_INCREMENT属性)。 在目前的实现中,下面一个语句:

CREATE TABLE tablename (
    colname SERIAL
);

等价于以下语句:

CREATE SEQUENCE tablename_colname_seq AS integer;
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约束避免意外地插入重复的值,但这个不是自动发生的)。最后,该序列被标记为属于该列,这样当列或表被删除时该序列也会被删除。

注意

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

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

类型名serialserial4是等效的: 两个都创建integer列。类型名bigserialserial8也一样,只不过它们创建一个 bigint列。如果你预计在表的生存期中使用的标识符数目超过 231 个,那么你应该使用bigserial。类型名smallserialserial2也以相同方式工作,只不过它们创建一个smallint列。

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