数值类型
INT
整数类型一般用INT表示,ZNBASE支持各种有符号整数数据类型。
需要注意的是:
要生成自动增长的全局唯一值(例如主键),使用UUID或SERIAL数据类型。
需要任意精度数字,使用DECIMAL数据类型。
别名和取值范围
整数类型有各种不同的名称和取值范围。各种整数类型名称还有别名。详情见下表。
名称 | 别名 | 占用位数(bit) | 取值范围 |
---|---|---|---|
INT | INTEGER INT8 INT64 BIGINT | 64 | -9223372036854775808 ~ +9223372036854775807 |
INT2 | SMALLINT | 16 | -32768 ~ +32767 |
INT4 | NONE | 32 | -2147483648 ~ +2147483647 |
INT8 | INT | 64 | -9223372036854775808 ~ +9223372036854775807 |
数值文本可以作为INT类型的输入。 例如: 42,-1234,或0xCAFE。
不同的整数类型对允许值的范围设置了不同的约束。但是无论类型如何,所有整数都以相同的方式存储。较小的值比较大的值占用更少的空间(基于数值,而不是数据类型)。
类型转换
整数类型的值可以强制转换为以下任何数据类型:
类型 | 描述 |
---|---|
DECIMAL | - |
FLOAT | 如果INT值大于2^53,则会丢失精度 |
BIT | 转换为整数值的二进制表示形式。如果该值为负,则在左侧复制符号位以填充整个位数组。 |
BOOL | 0转换为false; 所有其他值转换为true |
DATE | 自Unix时间(1970年1月1日)加上整数指定的天数,转换为日期时间。 |
TIMESTAMP | 自Unix时间(1970年1月1日)加上整数指定的秒数,转换为日期时间。 |
INTERVAL | 整数以秒为单位转换为时间。 |
STRING | - |
示例
示例1:创建具有整数类型列的数据库表。
CREATE TABLE ints(c1 INT PRIMARY KEY, c2 SMALLINT, c3 BIGINT, c4 INTEGER, c5 INT2, c6 INT4, c7 INT8, c8 INT64);
CREATE TABLE
SHOW COLUMNS FROM ints;
column_name | data_type | is_nullable | column_default | generation_expression | indices | is_hidden
+——————-+—————-+——————-+————————+———————————-+—————-+—————-+
c1 | INT | false | NULL | | {primary} | false
c2 | SMALLINT | true | NULL | | {} | false
c3 | BIGINT | true | NULL | | {} | false
c4 | INTEGER | true | NULL | | {} | false
c5 | INT2 | true | NULL | | {} | false
c6 | INT4 | true | NULL | | {} | false
c7 | INT8 | true | NULL | | {} | false
c8 | INT64 | true | NULL | | {} | false
示例2:整数类型与时间的转换。
查看距离Unix时间123天的时间。
SELECT 123::DATE;
DATE
+——————————————————+
1970-05-04 00:00:00+00:00
查看距离Unix时间3600秒的时间。
SELECT 3600::TIMESTAMP;
timestamp
+—————————————-+
1970-01-01 01:00:00+00:00
查看3600秒的时间格式。
SELECT 3600::INTERVAL;
interval
+——————+
01:00:00
示例3:整数类型与其他数值类型的转换。
整数类型与FLOAT类型的转换。
SELECT 3600::FLOAT;
float
+————+
3600
示例4:整数类型与BOOL类型的转换。
SELECT 3600::BOOL;
bool
+———+
true
SELECT 0::BOOL;
bool
+———-+
false
SELECT (-1)::BOOL;
bool
+———+
true
整数类型的0转换为BOOL类型的false;其他值都转换为true。
注意事项
需要注意的是,64位有符号整数默认情况下,INT的别名是INT8,它将创建64位有符号整数。如果您的应用程序不支持64位整数,则可能会出现问题。编写应用程序的语言,或用来生成SQL的ORM/框架(如果有),都有可能引入上述问题。
例如,JavaScript语言运行时将数字表示为64位浮点数,所以JS运行时只能表示53位数字精度,因此最大安全值为2的53次方(也就是9007199254740992)。这意味着ZNBASE中默认INT的最大值远远大于JavaScript的整数类型所能表示的最大值。显而易见,大小差异如下:
ZNBASE的INT类型默认的最大值:9223372036854775807;
JS的INTEGER类型的最大值:9007199254740991;
鉴于上述情况,如果表包含具有默认大小的INT值的列,并且正在从表中读取或通过JavaScript对其进行写入,则将无法正确地将值读取和写入该列。如果使用的是自动生成前端代码和后端代码的框架(例如twirp),则可能会以奇怪的方式弹出此问题。在这种情况下,你可能会发现ZNBASE数据库可以处理64位带符号整数,但生成的客户端/前端代码则不能。
如果应用程序需要使用与ZNBASE默认值不同的整数大小(包括但不限于上述原因),则可以更改以下一项或两项设置。例如,可以将下面参数中的任何一个设置为4,这样INT和SERIAL分别会成为INT4和SERIAL4的别名,它们将会使用32位整数。
会话变量:default_int_size。
集群环境参数:sql.defaults.default_int_size。
SERIAL
SERIAL关键字是一个伪数据类型,可以在定义表的列时代替实际的数据类型。使用SERIAL定义表的列,约等于带有DEFAULT表达式的整数类型,这个表达式在每次求值的时候会生成不同的值。SERIAL类型可以确保插入操作在未指定此列的值的时候可以自动生成一个值,而不是插入一个NULL值。
扩展和实际数据类型
SERIAL为了使用方便,进行了扩展。所有的SERIAL都是整数类型的。SERIAL的扩展和实际数据类型的对应关系如下表所示。
SERIAL扩展 | 实际数据类型 |
---|---|
SERIAL2, SMALLSERIAL | INT2 |
SERIAL4 | INT4 |
SERIAL | INT |
SERIAL8, BIGSERIAL | INT8 |
表中的每个INSERT或UPSERT操作都将调用nextval()函数,生成递增序列并产生递增的值。因为SQL序列将当前序列值保留在数据库中,所以当多个客户端通过不同节点同时使用序列时,需要节点间协调。这可能导致竞争并降低性能。因此,应用程序应考虑尽可能使用unique_rowid()或gen_random_uuid(),而不使用sequences。
注意:此模式sql_sequence是实验功能,用于测试与现有PostgreSQL客户端的兼容性。它可能随时更改,恕不另行通知,并且可能会在更高版本的ZNBASE中删除。
使用方式
关键字SERIAL可以在表创建过程中被CREATE TABLE识别,并自动转换为实际数据类型的DEFAULT表达式。该转换结果由ZNBASE在内部使用,可以使用SHOW CREATE观察这个行为。选择的DEFAULT表达式可确保在插入行数据时,自动为列生成不同的值。这些值不能保证单调增加。
方式 | 描述 |
---|---|
rowid (default) | SERIAL表示DEFAULT unique_rowid()。实际数据类型始终为INT。 |
virtual_sequence | SERIAL创建一个虚拟序列并表示DEFAULT nextval(<seqname>)。实际数据类型始终为INT。 |
sql_sequence | SERIAL创建一个常规SQL序列并表示DEFAULT nextval(<seqname>)。实际数据类型取决于SERIAL变量。 |
可以使用实验性会话变量experimental_serial_normalization 配置这些模式。
为rowid和virtual_sequence方式生成值
在rowid和virtual_sequence两种方式下,都会使用unique_rowid()函数自动生成一个值。这将从执行INSERT或UPSERT操作的节点的当前时间戳和ID生成64位整数。从统计上讲,此行为在全球范围内几乎是唯一的,除非是极端情况。另外,由于使用unique_rowid()生成值不需要节点间的协调,因此当多个SQL客户端从不同节点向表写入数据时,它比sql_sequence方式快得多。
rowid和virtual_sequence之间的区别在于,后者的设置还会在数据库中创建虚拟(伪)序列。但是,这两种情况最终都用unique_rowid()函数生成新值。
为sql_sequence模式生成的值
在这种模式下,指定SERIAL的列将与表一起自动创建一个常规SQL序列。SERIAL类型列的实际数据类型详见《扩展和实际数据类型》章节。
示例
示例1:使用SERIAL自动生成主键。
创建一个以SERIAL列为主键的表,在执行INSERT操作时自动生成唯一的ID。
CREATE TABLE serial (c1 SERIAL PRIMARY KEY, c2 STRING, c3 BOOL);
CREATE TABLE
SHOW COLUMNS FROM serial;
column_name | data_type | is_nullable | column_default | generation_expression | indices | is_hidden
+——————-+—————-+——————-+————————+———————————-+—————-+—————-+
c1 | SERIAL | false | unique_rowid() | | {primary} | false
c2 | STRING | true | NULL | | {} | false
c3 | BOOL | true | NULL | | {} | false
(3 rows)
SERIAL类型只是INT的别名,默认值为unique_rowid()的生成值。
插入一个新行,并把c1列空出。在显示新行时,每一行在c1列中均存在一个默认的唯一值。
INSERT INTO serial (c2,c3) VALUES (‘one’, true), (‘two’, false), (‘three’, true);
INSERT 3
INSERT INTO serial (c1,c2,c3) VALUES (110,’four’, true);
INSERT 1
SELECT * FROM serial;
c1 | c2 | c3
+——————————+———-+———-+
110 | four | true
505491594679386113 | one | true
505491594679451649 | two | false
505491594679484417 | three | true
(4 rows)
注意事项
当客户端使用SERIAL关键字时,DEFAULT表达式的特定选择在将来的InCLoudZNBASE版本中可能会发生变化。希望专门unique_rowid()的应用程序必须使用完整的显式语法INT DEFAULT unique_rowid()并完全避免使用SERIAL。此外,SERIAL的多种转换模式的存在InCloud ZNBASE的一项实验功能,旨在研究与现有PostgreSQL应用程序的兼容性,可能在后续版本中删除。
应用程序应使用实际数据类型和适当的DEFAULT表达式。在大多数情况下,建议将UUID数据类型与gen_random_uuid()函数一起使用作为默认值,该函数会生成128位值(大于SERIAL64位的最大值),并将它们更均匀地分散在表的所有基础键值范围内。当在索引或主键中使用UUID列时,UUID可以更有效地确保多个节点分散插入负载。
SERIAL的virtual_sequence和sql_sequence工作方式是试验性的,可能在后续版本的InCloud ZNBASE中删除。
SERIAL自动递增并不总是连续的。一个常见的误解是,InCloud ZNBASE中的自动递增类型会生成严格的顺序值。但是实际情况是,可能存在空白并且不能完全保证顺序:
每个INSERT或UPSERT操作即使没有提交,也会将序列值增加1。这意味着自动递增类型可能会在序列中留下空白。
两个并发事务可以以与使用序列不同的顺序提交。因此“观察”值相对于彼此减小。事务自动重试会放大这种效果。
这些是具有非事务序列的事务系统的基本属性。InCloud ZNBASE不会与其他SQL语句以事务方式增加序列,因此在任何情况下可能发生以上情况。
SERIAL自动递增不保证单调增加。如果同时发生两个事务,InCloud ZNBASE无法保证ID单调递增。原因如下:
SERIAL值是根据当前时间的微秒数构造的,如果两个事务的开始时间小于10毫秒,那么无法保证两个事务的ID大小顺序与先后顺序一致。可能出现增大、减小和相等中的任何情况。
两个事务在不同的节点上。无法保证两个事务的ID大小顺序与先后顺序一致。可能出现增大或减小的情况。但是,InCloud ZNBASE可以保证不同节点产生的两个ID不相等。
两个事务在不同的节点上,两个节点的系统时钟差异小于500毫秒,但是仍然存在始终偏斜,并且时间早晚不确定。
FLOAT
InCloud ZNBASE支持各种不确定精度的浮点数数据类型,最多可达到17位十进制小数精度,用FLOAT表示。浮点数在内部使用标准双精度(64位二进制编码)IEEE754格式处理。
名称和别名
浮点数在InCloud ZNBASE中的名称和别名如下表所示:
名称 | 别名 |
---|---|
FLOAT | None |
REAL | FLOAT4 |
DOUBLE PRECISION | FLOAT8 |
语法
数值文本可以作为浮点类型的输入。例如:1.414或1234。对于正无穷大、负无穷大和NaN(非数字)的特殊IEEE754值不能直接使用数值输入,必须使用解释文字或从字符串文字的显式转换进行转换。
IEEE754的特殊值如正无穷大、负无穷大和NaN(非数字),不能直接使用数字文字输入,而必须使用解释文字或从字符串文字的显式转换进行转换。
下表中的值可以被识别:
语法 | 值 |
---|---|
inf, infinity, +inf, +infinity | 正无穷大 |
-inf, -infinity | 负无穷大 |
nan | NaN(非数字) |
例如:
FLOAT ‘+Inf’
‘-Inf’::FLOAT
CAST(‘NaN’ AS FLOAT)
FLOAT列最多支持8个字节的值,但由于InCloud ZNBASE元数据,总存储大小可能会更大。
类型转换
FLOAT可以转换为以下任何数据类型:
类型 | 描述 |
---|---|
INT | 截断小数精度,要求值介于-2 ^ 63和2 ^ 63-1之间 |
DECIMAL | 如果值为NaN或+/- Inf,则报错。 |
BOOL | 0转换为false; 所有其他值转换为true |
STRING | — |
示例
示例1:创建具有浮点数列的表。
CREATE TABLE floats (c1 FLOAT PRIMARY KEY, c2 REAL, c3 DOUBLE PRECISION);
CREATE TABLE
SHOW COLUMNS FROM floats;
column_name | data_type | is_nullable | column_default | generation_expression | indices | is_hidden
+——————-+—————-+——————-+————————+———————————-+—————-+—————-+
c1 | FLOAT | false | NULL | | {primary} | false
c2 | REAL | true | NULL | | {} | false
c3 | DOUBLE PRECISION | true | NULL | | {} | false
示例2:向具有FLOAT列的表中插入值。
FLOAT列可以插入IEEE754的特殊值如正无穷大、负无穷大和NaN(非数字)。
INSERT INTO floats VALUES (3.141592653834231235345, 2.718281828459, CAST(‘+Inf’ AS FLOAT)),(FLOAT’inf’,1e1,’nan’::FLOAT);
INSERT 2
SELECT * FROM floats;
c1 | c2 | c3
+——————————+——————————+———+
-Inf | 10 | NaN 3.1415926538342314
2.7182817459106445 | +Inf |
DECIMAL
DECIMAL数据类型存储精确的定点数。当保留精确的精度很重要时可以使用此类型,例如,使用货币数据。
别名
在InCloud ZNBASE中,DECIMAL的别名有:
DEC
NUMERIC
数值文本可以作为DECIMAL类型的值输入。 例如:1.414或-1234。
对于正无穷大、负无穷大和NaN(非数字)的特殊IEEE754值不能直接使用数值输入,必须使用解释文字或从字符串文字的显式转换进行转换。
下表中的值可以被识别:
语法 | 值 |
---|---|
inf, infinity, +inf, +infinity | 正无穷大 |
-inf, -infinity | 负无穷大 |
nan | NaN(非数字) |
例如:
DECIMAL’+Inf’
‘-Inf’:: DECIMAL
CAST(‘NaN’ AS DECIMAL)
DECIMAL的存储长度从9字节开始,但是是可变的。建议将值保持在64k字节以下,以保证性能。如果超过该阈值,写入放大和其他因素可能导致明显的性能下降。
精度和取值范围
使用DECIMAL(precision,scale) 限制DECIMAL列,其中precision是小数点左侧和右侧之和的最大位数,scale是小数点右侧的精确位数。precision不能小于scale。使用DECIMAL(precision) 等效于 DECIMAL(precision, 0)。
当插入十进制(DECIMAL)值时:
如果小数点右边的数字位数超过列的scale,则InCloud ZNBASE将四舍五入到scale。
如果小数点右边的数字位数小于列的scale,则InCloud ZNBASE将填充到0直到scale。
如果小数点左侧和右侧的数字位数之和超过列的precision,则InCloud ZNBASE会报错。
如果列的precision和scale相同,则插入的值必须四舍五入到小于1。
类型转换
DECIMAL类型的值可以转换为以下类型:
类型 | 描述 |
---|---|
INT | 截断小数精度 |
FLOAT | 丢失精度。如果值太大,可能会四舍五入到+/-无穷大;如果值太小,则可能会四舍五入到+/- 0 |
BOOL | 0转换为false; 所有其他值转换为true |
STRING | — |
示例
示例1:创建具有DECIMAL列的表。
CREATE TABLE decimals(c1 DECIMAL PRIMARY KEY, c2 DEC, c3 NUMERIC, c4 DECIMAL(3), c5 DECIMAL(10, 2));
CREATE TABLE
SHOW COLUMNS FROM decimals;
column_name | data_type | is_nullable | column_default | generation_expression | indices | is_hidden
+——————-+———————-+——————-+————————+———————————-+—————-+—————-+
c1 | DECIMAL | false | NULL | | {primary} | false
c2 | DEC | true | NULL | | {} | false
c3 | NUMERIC | true | NULL | | {} | false
c4 | DECIMAL(3) | true | NULL | | {} | false
c5 | DECIMAL(10,2) | true | NULL | | {} | false
DEC和 NUMERIC的实际类型为DECIMAL。
示例2:向具有DECIMAL列的表中插入值。
DECIMAL列可以插入IEEE754的特殊值如正无穷大、负无穷大和NaN(非数字)。
INSERT INTO decimals VALUES (1.01234567890123456789, 1.01234567890123456789, 1.01234567890123456789),(DECIMAL’+inf’,’-inf’::DEC,CAST(‘nan’ AS NUMERIC));
INSERT 2
SELECT * FROM decimals;
c1 | c2 | c3 | c4
+————————————+—————-+————————————+———+
1.01234567890123456789 | 1.01235 | 1.01234567890123456789 | NULL
Infinity | -Infinity | NaN | NULL
c1列的值和DECIMAL精度匹配,完全保持原值。
c2列的值小数部分被四舍五入到与DECIMAL(10,5)的scale相同的5位。
c3列的NUMERIC是DECIMAL的别名,因此和c1具有相同的处理方式。