1. 数据类型
基础类型只有数值、字符串和时间三种类型,没有Boolean类型,但可以使用整型的0或1替代。
ClickHouse的数据类型和常见的其他存储系统的数据类型对比:
MySQL | Hive | CLickHouse(区分大小写) |
---|---|---|
byte | TINYINT | Int8 |
short | SMALLINT | Int16 |
int | INT | Int32 |
long | BIGINT | Int64 |
varchar | STRING | String |
timestamp | TIMESTAMP | DateTime |
float | FLOAT | Float32 |
double | DOUBLE | Float64 |
boolean | BOOLEAN | 无 |
更多可查看官网:https://clickhouse.tech/docs/zh/sql-reference/data-types/
1.1.数值类型
1.1.1.整数
整型范围:( 到
):
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 : 2147483647]
Int64 - [-9223372036854775808 : 9223372036854775807]
无符号整型范围( 0到 ):
UInt8 - [0 : 255]
UInt16 - [0 : 65535]
UInt32 - [0 : 4294967295]
UInt64 - [0 : 18446744073709551615]
1.1.2.浮点型
与整数类似,ClickHouse 直接使用 Float32 和 Float64 代表单精度浮点数以及双精度浮点数
建议尽可能以整数形式存储数据。例如,将固定精度的数字转换为整数值,如时间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差。
hadoop1 :) select 1 - 0.9;
SELECT 1 - 0.9
┌───────minus(1, 0.9)─┐
│ 0.09999999999999998 │
└─────────────────────┘1 rows in set. Elapsed: 0.010 sec.
比较特殊的情况:
1.inf 表示正无穷;-inf表示负无穷;nan表示非数字。
hadoop1 :) select -1/0;
SELECT -1 / 0
┌─divide(-1, 0)─┐
│ -inf │
└───────────────┘
hadoop1 :) select 0/0;
SELECT 0 / 0
┌─divide(0, 0)─┐
│ nan │
└──────────────┘
2. Float32 从小数点后第 8 位起及 Float64 从小数点后第 17 位起,都产生了数据溢出,会进行舍弃。
hadoop1 :) select toFloat64('0.1234567901234567890123456789') as a,
:-] toTypeName(a);SELECT
toFloat64('0.1234567901234567890123456789') AS a,
toTypeName(a)┌───────────────────a─┬─toTypeName(toFloat64('0.1234567901234567890123456789'))─┐
│ 0.12345679012345678 │ Float64 │
└─────────────────────┴─────────────────────────────────────────────────────────┘
1.1.3.Decimal
如果要求更高精度的数值运算,则需要使用定点数。ClickHouse 提供了Decimal32、Decimal64 和Decimal128 三种精度的定点数。可以通过两种形式声明定点:简写方式有Decimal32(S)、Decimal64(S)、Decimal128(S)三种,原生方式为Decimal(P, S),其中:
P代表精度,决定总位数(整数部分+小数部分),取值范围是1~38;
S代表规模,决定小数位数,取值范围是0~P。
简写方式与原生方式的对应关系:
名称 | 等效声明 | 范围 |
---|---|---|
Decimal32(S) | Decimal(1~9, S) | -1 * 10^(9-S) 到 1 * 10^(9-S) |
Decimal64(S) | Decimal(10~18, S) | -1 * 10^(18-S) 到 1 * 10^(18-S) |
Decimal128(S) | Decimal(19~38, S) | -1 * 10^(38-S) 到 1 * 10^(38-S) |
在使用两个不同精度的定点数进行四则运算的时候,它们的小数点位数S会发生变化。总结如下:
名称 | 规则 |
---|---|
加法 | S = max(S1, S2) |
减法 | S = max(S1, S2) |
乘法 | S = S1 + S2(S1范围 >= S2范围) |
除法 | S = S1(S1为被除数,S1/S2) |
1.1.4.布尔类型
没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1。而且在 ClickHouse 使用过程中,你也会发现,做比较得到的结果都是 1 或者 0,而不是通常意义上的 True 或者 False。
hadoop1 :) select 1 == 1;
SELECT 1 = 1
┌─equals(1, 1)─┐
│ 1 │
└──────────────┘1 rows in set. Elapsed: 0.005 sec.
hadoop1 :) select 1==2;
SELECT 1 = 2
┌─equals(1, 2)─┐
│ 0 │
└──────────────┘
1.2.字符串
字符串类型可以细分为 String、FixedString 和 UUID 三类。
1.2.1. String类型
字符串可以任意长度的。它可以包含任意的字节集,包含空字节,可以用来替换 VARCHAR,BLOB,CLOB 等数据类型。字符串由String定义,长度不限。因此在使用String的时候无须声明大小。它完全代替了传统意义上数据库的Varchar、Text、Clob和Blob等字符类型。String类型不限定字符集,因为它根本就没有这个概念,所以可以将任意编码的字符串存入其中。但是为了程序的规范性和可维护性,在同一套程序中应该遵循使用统一的编码,例如“统一保持UTF-8编码”就是一种很好的约定。
1.2.2.FixedString类型
FixedString 类型和传统意义上的 Char 类型有些类似,对于一些字符有明确长度的场合,可以使用固定长度的字符串。定长字符串通过 FixedString(N) 声明,其中 N 表示字符串长度。但与 Char 不同的是,FixedString 使用 null字节填充末尾字符,而 Char 通常使用空格填充。比如在下面的例子中,字符串‘abc’ 虽然只有 3 位,但长度却是5,因为末尾有2位空字符填充:
SELECT toFixedString('abc', 5), LENGTH(toFixedString ('abc',5))AS LENGTH;
固定长度 N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于 N 的字符串时候,通过在字符串末尾添加空字节来达到 N 字节长度。 当服务端读取长度大于 N 的字符串时候,将返回错误消息。与 String 相比,极少会使用 FixedString,因为使用起来不是很方便。
总结:
- N是最大字节数(Byte),不是字符长度,如果是UTF8字符串,那么就会占用3字节,GBK会占用2字节.
- 当内容少于N,数据库会自动在右填充空字节(null byte)(跟PGsql不一样,PGsql填充的是空格),当内容大于N时候,会抛出错误.
- 当写入内容后面后空字节,系统不会自动去裁剪,查询的时候也会被输出(mysql不会输出)
- FixedString(N) 比 String 支持更少的方法
1.2.3.UUID
UUID 是一种数据库常见的主键类型,在 ClickHouse 中直接把它作为一种数据类型。UUID 共有 32位,它的格式为8-4-4-4-12。如果一个 UUID 类型的字段在写入数据时没有被赋值,则会依照格式使用0(00000000-0000-0000-0000-000000000000)填充。
1.3. 日期时间类型
时间类型分为 DateTime、DateTime64 和 Date 三类。ClickHouse 目前没有时间戳类型。时间类型最高的精度是秒,也就是说,如果需要处理毫秒、微秒等大于秒分辨率的时间,则只能借助 UInt 类型实现。
1.3.1.Date类型
日期类型,用两个字节存储,表示从 1970-01-01 (无符号) 到当前的日期值。允许存储从 Unix 纪元开始到编译阶段定义的上限阈值常量(目前上限是2106年,但最终完全支持的年份为2105)。最小值输出为1970-01-01。
Date类型不包含具体的时间信息,只精确到天,它支持字符串形式写入。即使写入的为yyyy-MM-dd HH:mm:ss,也会将其截取为yyyy-MM-dd。
1.3.2. DateTime类型
时间戳类型。用四个字节(无符号的整数类型,Uint32)存储 Unix 时间戳。允许存储与 Date 类型相同的范围内的值。最小值为 1970-01-01 00:00:00。时间戳类型值精确到秒(没有闰秒)。
1.3.3.DateTime64类型
DateTime64 可以记录亚秒,它在 DateTime 之上增加了精度的设置,例如:
1.4.复合类型
1.4.1. 枚举类型
ClickHouse 支持枚举类型,这是一种在定义常量时经常会使用的数据类型。ClickHouse 提供了Enum8 和Enum16 两种枚举类型,它们除了取值范围不同之外,别无二致。枚举固定使用(String:Int)Key/Value键值对的形式定义数据,所以Enum8和Enum16分别会对应(String:Int8) 和 (String:Int16)包括 Enum8 和 Enum16 类型。Enum 保存 'string'= integer 的对应关系。建表的时候 Key/Value 是不允许重复的。其次, Key/Value 不能同时为 Null ,但是Key允许空字符串。
在定义枚举集合的时候,有几点需要注意。首先,Key和Value是不允许重复的,要保证唯一性。其次,Key和Value的值都不能为Null,但Key允许是空字符串。在写入枚举数据的时候,只会用到Key字符串部分。
使用枚举的优势:出于性能的考虑。因为虽然枚举定义中的Key属于String类型,但是在后续对枚举的所有操作中(包括排序、分组、去重、过滤等),会使用Int类型的Value值。
案例:
CREATE TABLE my_enum_test (x Enum8('a' = 1, 'b' = 2, 'c'=3)) ENGINE = TinyLog;
INSERT INTO my_enum_test VALUES ('a'), ('b'), ('c');
SELECT CAST(x, 'Int8') FROM my_enum_test ;
1.4.2.数组
array[T]:T 可以是任意类型,包含数组类型。 但不推荐使用多维数组,ClickHouse 对多维数组的支持有限。例如,不能存储在 MergeTree 表中存储多维数组。
SELECT array(1, 2) AS x, toTypeName(x);
SELECT [1, 2] AS x, toTypeName(x);
ClickHouse 会自动检测数组元素,并根据元素计算出存储这些元素最小的数据类型。如果在元素中存在 Null 或存在 Nullable 类型元素,那么数组的元素类型将会变成 Nullable 。
如果 ClickHouse 无法确定数据类型,它将产生异常。当尝试同时创建一个包含字符串和数字的数组时会发生这种情况 ( SELECT array(1, 'a') )。
1.4.3. 元组
元组类型由1~n个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容。元组同样支持类型推断,其推断依据仍然以最小存储代价为原则。与数组类似,元组也可以使用两种方式定义,常规方式tuple(T)。
元素类型和泛型的作用类似,可以进一步保障数据质量。在数据写入的过程中会进行类型检查。Tuple(T1, T2, ...):元组,其中每个元素都有单独的类型。
CREATE TABLE my_tuple_table (t Tuple(Int8, String, Array(String), Array(Int8))) ENGINE = TinyLog;
INSERT INTO my_tuple_table VALUES((1, 'a', ['a', 'b', 'c'], [1, 2, 3])),(tuple(11, 'A', ['A', 'B', 'C'], [11, 22, 33]));
SELECT t, t.2, t.3, t.4 FROM my_tuple_table;
1.4.4. 嵌套类型
嵌套类型,顾名思义是一种嵌套表结构。一张数据表,可以定义任意多个嵌套类型字段,但每个字段的嵌套层级只支持一级,即嵌套表内不能继续使用嵌套类型。对于简单场景的层级关系或关联关系,使用嵌套类型也是一种不错的选择。
嵌套Nested(Name1 Type1,Name2 Type2,…)
嵌套的数据结构就像一个嵌套的表。嵌套数据结构的参数 - 列名和类型 - 与在CREATE查询中的指定方式相同。每个表的行可以对应于嵌套数据结构中的任意数量的行。
drop table my_nest_table;
CREATE TABLE my_nest_table (
name String,
age Int8,
dept Nested(
id UInt8,
name String
)
)
ENGINE = TinyLog;
insert into my_nest_table values ('张三', 18, [1], ['大数据组']);
insert into my_nest_table values ('李四', 18, [1,2,3],['hadoop','spark','flink']);
1.5.其它类型
1.5.1. Nullable(TypeName)
准确来说,Nullable 并不能算是一种独立的数据类型,它更像是一种辅助的修饰符,需要与基础数据类型一起搭配使用。Nullable 类型与 Java8 的 Optional 对象有些相似,它表示某个基础数据类型可以是Null 值。
许用特殊标记 (NULL) 表示 "缺失值",可以与 TypeName 的正常值存放一起。例如,Nullable(Int8) 类型的列可以存储 Int8 类型值,而没有值的行将存储 NULL。
特点:
1、Nullable 只能和基本类型搭配使用;
2、不能使用在 Array/Tuple 这种复合类型上;
3、不能作为索引字段【Order by()】;
4、慎用 Nullable ,写入写出性能不好。因为它会生成单独的文件。
create table null_test(
c1 String,
c2 Nullable(UInt8)
) engine = TinyLog;
在使用 Nullable 类型的时候还有两点值得注意:首先,它只能和基础类型搭配使用,不能用于数组和元组这些复合类型,也不能作为索引字段;其次,应该慎用 Nullable 类型,包括 Nullable 的数据表,不然会使查询和写入性能变慢。因为在正常情况下,每个列字段的数据会被存储在对应的 [Column].bin文件中。如果一个列字段被Nullable 类型修饰后,会额外生成一个 [Column].null.bin 文件专门保存它的 Null 值。这意味着在读取和写入数据时,需要一倍的额外文件操作。
1.5.2. Domain
域名类型分为 IPv4 和 IPv6 两类,本质上它们是对整型和字符串的进一步封装。IPv4 类型是基于UInt32 封装的,它的具体用法如下所示:
分为 IPv4 IPv6 。其实本质都是对整型,字符串进行的封装。
IPv4 使用 UInt32 存储。如116.253.40.133
IPv6 使用 FixedString(16) 存储。如2a02:aa08:e000:3100::2
问题:为什么不直接使用字符串来代替 IPv4 类型呢?
1、出于便捷性的考量,例如IPv4类型支持格式检查,格式错误的IP数据是无法被写入的
2、出于性能的考量,同样以IPv4为例,IPv4使用UInt32存储,相比String更加紧凑,占用的空间更小,查询性能更快。IPv6类型是基于FixedString(16)封装的,它的使用方法与IPv4别无二致
在使用Domain类型的时候还有一点需要注意,虽然它从表象上看起来与String一样,但Domain类型并不是字符串,所以它不支持隐式的自动类型转换。如果需要返回IP的字符串形式,则需要显式调用IPv4NumToString或IPv6NumToString函数进行转换。
1.5.3.default
c1 DEFALUT 100 给定默认值 根据这个来判断类型
c2 String DEFALUT c1 c2类型是String,c2的默认值是c1的