数据类型
ClickHouse 的数据类型大致划分为:基本数据类型,复合数据类型,特殊类型。
和普遍使用的 Mysql 进行比较。
基本类型
只有数值,字符串,时间。没有 Boolean ,但可以通过 0/1 来替代。
| 基本类型 | 对应 Mysql |
|---|---|
Int8 |
Tinyint |
Int16 |
Smallint |
Int32 |
Int |
Int64 |
Bigint |
Float32 |
Float |
Float64 |
Double |
String |
Varchar Text Blob |
FixedString(N) |
Char |
UUID [8-4-4-4-12] |
无 |
DateTime [到秒] |
|
DateTime64 [到亚秒] |
|
Date [到天] |
复合类型
提供了数组,元组,枚举,嵌套四种复合类型。
Array
select [1,2,Null] as a, toTypeName(a);
┌─a──────────┬─toTypeName([1, 2, NULL])─┐
│ [1,2,NULL] │ Array(Nullable(UInt8)) │
└────────────┴────────────────┘
一个数组里面时可以包含多个数据类型,但是多个数据类型必须兼容,[1, 'click'] 这种的就会报错。所以在建表建议标明字段类型【也建议在建表的时候将所以的字段名类型和注释都标注清楚】:
create table test (
c1 Array(String)
) engine = TinyLog;
Tuple
SELECT
(1, 'click', now()) AS a,
toTypeName(a)
┌─a───────────────────┬─toTypeName(tuple(1, 'click', now()))─┐
│ (1,'click','2020-06-06 05:59:57') │ Tuple(UInt8, String, DateTime) │
└───────────────────────────────────┴─────────┘
从这里可以看出,元组是支持不同的数据类型,彼此之间不需要兼容。所以在建表的时候也建议指定字段类型。
Enum
定义常量时会经常使用该类型,分 Enum8/Enum16。Enum 固定使用 Enum(String=Int) 的键值对格式。
CREATE TABLE test (
`c1` Enum('click' = 1, 'house' = 2, 'java' = 3)
) ENGINE = TinyLog
insert into test values ('click');
insert into test values ('house');
建表的时候 Key/Value 是不允许重复的。其次,Key/Value 不能同时为 Null ,但是Key允许空字符串。
Nested
CREATE TABLE test_nest (
`name` String,
`age` Int8,
`dept` Nested(
id UInt8,
name String
)
)
ENGINE = TinyLog
但是实际上 Nested 会传统的嵌套类型不一样,不能理解为 test_nest 和 dept 不是一对一的结构:
insert into test_nest values ('nest', 18, 1000, '大数据组')
Exception on client:
Code: 53. DB::Exception: Type mismatch in IN or VALUES section. Expected: Array(UInt8). Got: UInt64
异常显示需要 Array,而不是单纯的 Int。所以这里也就明白:嵌套类型本质是一个多维数组的结构。
嵌套类型的一个字段对应一个数组。字段对应的数组内的数量没有限制,但是字段之间需要数组内的数量对齐。
SELECT
name,
dept.id,
dept.name
FROM test_nest
┌─name─┬─dept.id───────┬─dept.name───────────────┐
│ nest │ [232,233,235] │ ['clickhouse','kafka','debezium'] │
│ tube │ [7,9,10] │ ['flink','spark','kudu'] │
└────┴─────────────┴─────────────────────┘
访问的时候和 struct 的访问方式一致。
特殊类型
Nullable
Nullable 类似 java 的 Optional,表示某个基本类型可以为 Null。
从上面的介绍:
-
Nullable只能和基本类型搭配使用; -
不能使用在
Array/Tuple这种复合类型上; -
不能作为索引字段【
Order by()】; -
慎用
Nullable,写入写出性能不好。因为它会生成单独的文件。【这个和
Mysql的设计类似,Null也需要额外的空间存储,只是mysql是存储在每一个记录的头中。】
Domain
分为 IPv4 IPv6。其实本质都是对整型,字符串进行的封装。
IPv4使用UInt32存储。IPv6使用FixedString(16)存储。
可能会问,为什么不就直接使用整型和String存储呢?
Domain类型支持格式检查,错误的IP数据是不被允许写入到数据库中;IPv4不使用String存储,使用空间更小,查询性能更快。
虽然本质是使用String存储,但是类型上是不属于 String ,所以不支持隐形的类型转换。如果你需要输出IP字符串形式,需要 IPv4NumToString() 进行显示转换。