跳到主要内容

数值类型

固定宽度整数类型

TINYINTSMALLINTINTEGERBIGINTHUGEINT 用于存储整数(即不包含小数部分的数字),各自支持不同范围。若写入超出允许范围的值,会报错。 UTINYINTUSMALLINTUINTEGERUBIGINTUHUGEINT 用于存储无符号整数。若写入负数或超出允许范围的值,会报错。

名称别名最小值最大值大小(字节)
TINYINTINT1- 2^72^7 - 11
SMALLINTINT2, INT16, SHORT- 2^152^15 - 12
INTEGERINT4, INT32, INT, SIGNED- 2^312^31 - 14
BIGINTINT8, INT64, LONG- 2^632^63 - 18
HUGEINTINT128- 2^1272^127 - 116
UTINYINTUINT802^8 - 11
USMALLINTUINT1602^16 - 12
UINTEGERUINT3202^32 - 14
UBIGINTUINT6402^64 - 18
UHUGEINTUINT12802^128 - 116

INT8 是 64 位整数,并不是 UINT8(无符号 8 位整数)的有符号对应类型。INT1INT2INT4INT8 这些有符号整数别名继承自 PostgreSQL,在该命名中数字表示 字节数;而无符号对应别名 UINT8UINT16UINT32UINT64 采用 C/C++ 习惯,数字表示 位数

INTEGER 通常是最常见选择,在范围、存储大小和性能之间平衡较好。SMALLINT 一般只在磁盘空间极其紧张时使用。BIGINTHUGEINT 适用于 INTEGER 范围不足的场景。

可变长度整数

前面提到的整数类型有一个共同点:其最小值到最大值范围内的所有数字都使用固定存储大小,例如 UTINYINT 为 1 字节、SMALLINT 为 2 字节等。 但有时你需要比 HUGEINT 还大的整数。在这种情况下可使用 BIGNUM 类型。它对正数的存储方式与其他整数类型类似,但会额外使用 3 个字节来存储所需长度与符号位。一个有 N 位十进制数字的数,存储为 BIGNUM 时大约需要 0.415 * N + 3 字节。

与其他系统中的可变长度整数实现不同,BIGNUM 也有上限:可表示的最大/最小值约为 ±4.27e20201778。这相当于 20,201,779 位十进制数字,单个此类数字约需 8 MB 存储空间。

定点小数

DECIMAL(WIDTH, SCALE)(别名 NUMERIC(WIDTH, SCALE))表示精确的定点小数值。创建 DECIMAL 值时,可通过 WIDTHSCALE 指定该字段可容纳的小数范围。WIDTH 决定总位数,SCALE 决定小数点后的位数。例如 DECIMAL(3, 2) 可表示 1.23,但不能表示 12.31.234。若未指定,默认是 DECIMAL(18, 3)

两个定点小数做加减乘会返回另一个定点小数,其 WIDTHSCALE 会被调整为足以容纳精确结果;若所需 WIDTH 超过当前支持上限 38,则会报错。

定点小数相除通常不会产生有限小数。因此,Goose 对涉及定点小数的除法统一使用近似的浮点运算,并返回浮点类型结果。

在内部,DECIMAL 会根据 WIDTH 使用不同整数类型存储。

宽度内部类型大小(字节)
1-4INT162
5-9INT324
10-18INT648
19-38INT12816

在不需要的情况下使用过大 DECIMAL 会影响性能。尤其是宽度大于 19 的 DECIMAL 较慢,因为 INT128 运算成本明显高于 INT32INT64。因此除非确有必要,建议将 WIDTH 控制在 18 或以下。

浮点类型

FLOATDOUBLE 是可变精度的数值类型。实际中,这些类型通常实现为 IEEE 754 二进制浮点标准(分别对应单精度和双精度),具体取决于底层处理器、操作系统和编译器支持程度。

名称别名描述
FLOATFLOAT4, REAL单精度浮点数(4 字节)
DOUBLEFLOAT8双精度浮点数(8 字节)

与定点类型类似,从字面量转换或从其他类型 cast 到浮点类型时,无法精确表示的输入会被近似存储。但哪些输入会受影响往往更难直观判断。例如,1.3::DECIMAL(1, 0) - 0.7::DECIMAL(1, 0) != 0.6::DECIMAL(1, 0) 并不令人意外,但 1.3::FLOAT - 0.7::FLOAT != 0.6::FLOAT 可能会让人意外。

此外,定点小数的加减乘是精确的,而在二进制浮点类型上这些运算都只是近似结果。

不过,对于更复杂的数学运算,内部通常使用浮点运算;若中间步骤 被 cast 回与输入/输出同宽度的定点格式,通常能获得更精确结果。例如,(10::FLOAT / 3::FLOAT)::FLOAT * 3 = 10,而 (10::DECIMAL(18, 3) / 3::DECIMAL(18, 3))::DECIMAL(18, 3) * 3 = 9.999

一般来说:

  • 如果你需要精确存储具有确定小数位数的数值,并且要求加减乘精确(如金额),请使用 DECIMAL 类型或其别名 NUMERIC
  • 如果你需要快速或复杂计算,浮点类型可能更合适。但若结果用于关键场景,建议仔细评估实现中的边界情况(范围、无穷大、下溢、非法操作等),这些处理方式可能与你预期不同,并应了解常见浮点陷阱。可参考 David Goldberg 的文章 “What Every Computer Scientist Should Know About Floating-Point Arithmetic” 以及 Bruce Dawson 博客中的浮点系列

在大多数平台上,FLOAT 的范围至少为 1E-37 到 1E+37,精度至少约 6 位十进制;DOUBLE 的典型范围约为 1E-307 到 1E+308,精度至少约 15 位。超出这些范围的正数(以及对应镜像范围外的负数)在某些平台可能报错,但通常会分别转换为 0 或无穷大。

除普通数值外,浮点类型还支持多个 IEEE 754 特殊值:

  • Infinity:正无穷
  • -Infinity:负无穷
  • NaN:非数值

在具备所需 CPU/FPU 支持的机器上,Goose 对这些特殊值基本遵循 IEEE 754,但有两个例外:

  • NaNNaN 比较为相等,且大于任何其他浮点数。
  • 某些浮点函数(如 sqrt / sin / asin)对超出定义域的值会报错,而不是返回 NaN

在 SQL 中以字面量插入这些值时,必须加引号;Infinity 可缩写为 Inf;大小写不限。例如:

SELECT
sqrt(2) > '-inf',
'nan' > sqrt(2);
(sqrt(2) > '-inf')('nan' > sqrt(2))
truetrue

全局唯一标识符(UUID

Goose 通过 UUID 类型支持全局唯一标识符(UUID)。 UUID 使用 128 位,在内部表示为 HUGEINT 值。 输出时使用小写十六进制并以连字符分隔,格式为:⟨12345678⟩-⟨1234⟩-⟨1234⟩-⟨1234⟩-⟨1234567890ab⟩ (含连字符共 36 个字符)。例如,4ac7a9e9-607c-4c8a-84f3-843f0191e3fd 是合法 UUID。

Goose 支持生成 UUIDv4 与 UUIDv7。 要获取 UUID 值的版本,可使用 uuid_extract_version function

UUIDv4

生成 UUIDv4 可使用 uuid() function,或其别名 uuidv4()gen_random_uuid()

UUIDv7

生成 UUIDv7 可使用 uuidv7() function。 要从 UUIDv7 值中提取时间戳,可使用 uuid_extract_timestamp function

SELECT uuid_extract_timestamp(uuidv7()) AS ts;
ts
2025-04-19 15:51:20.07+00

函数

参见 Numeric Functions and Operators