前提
前边一篇文章详细分析了如何在Windows10
系统下搭建ClickHouse
的开发环境,接着需要详细学习一下此数据库的数据定义,包括数据类型、DDL
和DML
。ClickHouse
作为一款完备的DBMS
,提供了类似于MySQL
(其实有部分语法差别还是比较大的)的DDL
与DML
功能,并且实现了大部分标准SQL
规范中的内容。系统学习ClickHouse
的数据定义能够帮助开发者更深刻地理解和使用ClickHouse
。本文大纲(右侧分支)??
本文会详细分析ClickHouse
目前最新版本(20.10.3.30
)支持的所有数据类型。
数据类型
ClickHouse
的数据类型从大体的来看主要包括:
- 数值类型
- 字符串类型
- 日期时间类型
- 复合类型
- 特殊类型
这里做一份汇总的表格?
大类 | 类型 | 类型名称 | 一般概念 | JavaType | 备注 |
---|---|---|---|---|---|
数值类型 | Int8 | 8bit整型 | TINYINT | Byte|Integer | - |
数值类型 | Int16 | 16bit整型 | SMALLINT | Short|Integer | - |
数值类型 | Int32 | 32bit整型 | INT | Integer | - |
数值类型 | Int64 | 64bit整型 | BIGINT | Long | - |
数值类型 | Int128 | 128bit整型 | `- | - | - |
数值类型 | Int256 | 256bit整型 | - | - | - |
数值类型 | UInt8 | 无符号8bit整型 | TINYINT UNSIGNED | - | Java中不存在无符号整数类型,选择类型时只要不溢出就行 |
数值类型 | UInt16 | 无符号16bit整型 | SMALLINT UNSIGNED | - | Java中不存在无符号整数类型,选择类型时只要不溢出就行 |
数值类型 | UInt32 | 无符号32bit整型 | INT UNSIGNED | - | Java中不存在无符号整数类型,选择类型时只要不溢出就行 |
数值类型 | UInt64 | 无符号64bit整型 | BIGINT UNSIGNED | - | Java中不存在无符号整数类型,选择类型时只要不溢出就行 |
数值类型 | Float32 | 32bit单精度浮点数 | FLOAT | Float | - |
数值类型 | Float64 | 64bit双精度浮点数 | DOUBLE | Double | - |
数值类型 | Decimal(P,S) | 高精度数值,P为总位长,S为小数位长 | DECIMAL | BigDecimal | - |
数值类型 | Decimal32(S) | 高精度数值,P总位长属于[1,9],S为小数位长 | DECIMAL | BigDecimal | Decimal(P,S)特化类型 |
数值类型 | Decimal64(S) | 高精度数值,P总位长属于[10,18],S为小数位长 | DECIMAL | BigDecimal | Decimal(P,S)特化类型 |
数值类型 | Decimal128(S) | 高精度数值,P总位长属于[19,38],S为小数位长 | DECIMAL | BigDecimal | Decimal(P,S)特化类型 |
字符串类型 | String | 不定长字符串,长度随意不限 | 广义上类似LONGTEXT | String | 替代了传统DBMS中的VARCHAR、BLOB、CLOB、TEXT等类型 |
字符串类型 | FixedString(N) | 定长字符串,使用null字节填充末尾字符 | 有点类似VARCHAR | String | - |
字符串类型 | UUID | 特殊字符串,32位长度,格式为:8-4-4-4-4-12 | - | String | 一般使用内置函数生成 |
日期时间类型 | Date | 日期 | DATE | LocalDate | - |
日期时间类型 | DateTime | 日期时间 | 类似DATE_TIME | LocalDateTime | OffsetDateTime | 只精确到秒,不包含毫秒 |
日期时间类型 | DateTime64 | 日期时间 | 类似DATE_TIME | LocalDateTime | OffsetDateTime | 只精确到秒,不包含毫秒,但是包含亚秒,即10 ^ (-n)秒 |
复合类型 | Array(T) | 数组 | - | 类似T[] | - |
复合类型 | Tuple(S,T...R) | 元组 | - | - | - |
复合类型 | Enum | 枚举 | - | - | - |
复合类型 | Nested | 嵌套 | - | - | - |
特殊类型 | Nullable | NULL修饰类型,不是独立的数据类型 | - | - | - |
特殊类型 | Domain | 域名 | - | - | 存储IPV4和IPV6格式的域名 |
ClickHouse中类型严格区分大小写,一般为驼峰表示,例如DateTime不能写成DATETIME或者DATE_TIME,同理,UUID不能写成uuid
下面就每种类型再详细分析其用法。
数值类型
数值类型主要包括整型数值、浮点数值、高精度数值和特殊的布尔值。
整型
整型数值指固定长度(bit
数)的整数,可以使用带符号和无符号的表示方式。先看整型数值的表示范围??
带符号整型数值:
类型 | 字节(byte)数 | 范围 |
---|---|---|
Int8 | 1 | [-128, 127] |
Int16 | 2 | [-32768, 32767] |
Int32 | 4 | [-2147483648, 2147483647] |
Int64 | 8 | [-9223372036854775808, 9223372036854775807] |
Int128 | 16 | [-170141183460469231731687303715884105728, 170141183460469231731687303715884105727] |
Int256 | 32 | [-57896044618658097711785492504343953926634992332820282019728792003956564819968,57896044618658097711785492504343953926634992332820282019728792003956564819967] |
Int128和Int256能表示的整数范围十分巨大,占用的字节大小也随之增大,一般很少使用。
无符号整型数值:
类型 | 字节(byte)数 | 范围 |
---|---|---|
UInt8 | 1 | [0, 255] |
UInt16 | 2 | [0, 65535] |
UInt32 | 4 | [0, 4294967295] |
UInt64 | 8 | [0, 18446744073709551615] |
UInt256 | 32 | [0, 115792089237316195423570985008687907853269984665640564039457584007913129639935] |
值得注意的是,UInt128类型并不支持,因此不存在UInt128。UInt256能表示的整数范围十分巨大,占用的字节大小也随之增大,一般很少使用。
一般在使用MySQL
的时候会定义一个BIGINT UNSIGNED
类型的自增趋势的主键,在ClickHouse
中对标UInt64
类型。做一个小测试,在ClickHouse
命令行客户端中执行:
COPYSELECT
toInt8(127) AS a,toTypeName(a) AS aType,
toInt16(32767) AS b,toTypeName(b) AS bType,
toInt32(2147483647) AS c,toTypeName(c) AS cType,
toInt64(9223372036854775807) AS d,toTypeName(d) AS dType,
toInt128(170141183460469231731687303715884105727) AS e,toTypeName(e) AS eType,
toInt256(57896044618658097711785492504343953926634992332820282019728792003956564819967) AS f,toTypeName(f) AS fType,
toUInt8(255) AS g,toTypeName(g) AS gType,
toUInt16(65535) AS h,toTypeName(h) AS hType,
toUInt32(4294967295) AS i,toTypeName(i) AS iType,
toUInt64(18446744073709551615) AS j,toTypeName(j) AS jType,
toUInt256(115792089237316195423570985008687907853269984665640564039457584007913129639935) AS k,toTypeName(k) AS kType;
输出结果:
代码语言:javascript复制COPYSELECT
toInt8(127) AS a,
toTypeName(a) AS aType,
toInt16(32767) AS b,
toTypeName(b) AS bType,
toInt32(2147483647) AS c,
toTypeName(c) AS cType,
toInt64(9223372036854775807) AS d,
toTypeName(d) AS dType,
toInt128(1.7014118346046923e38) AS e,
toTypeName(e) AS eType,
toInt256(5.78960446186581e76) AS f,
toTypeName(f) AS fType,
toUInt8(255) AS g,
toTypeName(g) AS gType,
toUInt16(65535) AS h,
toTypeName(h) AS hType,
toUInt32(4294967295) AS i,
toTypeName(i) AS iType,
toUInt64(18446744073709551615) AS j,
toTypeName(j) AS jType,
toUInt256(1.157920892373162e77) AS k,
toTypeName(k) AS kType
┌───a─┬─aType─┬─────b─┬─bType─┬──────────c─┬─cType─┬───────────────────d─┬─dType─┬────────────────────────────────────────e─┬─eType──┬────────────────────f─┬─fType──┬───g─┬─gType─┬─────h─┬─hType──┬──────────i─┬─iType──┬────────────────────j─┬─jType──┬──────────────────────────────────────────────────────────────────────────────k─┬─kType───┐
│ 127 │ Int8 │ 32767 │ Int16 │ 2147483647 │ Int32 │ 9223372036854775807 │ Int64 │ -170141183460469231731687303715884105728 │ Int128 │ -9223372036854775808 │ Int256 │ 255 │ UInt8 │ 65535 │ UInt16 │ 4294967295 │ UInt32 │ 18446744073709551615 │ UInt64 │ 115792089237316195423570985008687907853269984665640564039448360635876274864128 │ UInt256 │
└─────┴───────┴───────┴───────┴────────────┴───────┴─────────────────────┴───────┴──────────────────────────────────────────┴────────┴──────────────────────┴────────┴─────┴───────┴───────┴────────┴────────────┴────────┴──────────────────────┴────────┴────────────────────────────────────────────────────────────────────────────────┴─────────┘
1 rows in set. Elapsed: 0.009 sec.
尴尬,上面的shell
执行结果有点长,变形了。
浮点数
浮点数包括单精度浮点数Float32
和双精度浮点数Float64
??
类型 | 字节(byte)大小 | 有效精度(排除最左边的零小数位数) | 备注 |
---|---|---|---|
Float32 | 4 | 7 | 小数点后除去左边的零后第8位起会产生数据溢出 |
Float64 | 8 | 16 | 小数点后除去左边的零后第17位起会产生数据溢出 |
可以做一个小测试:
代码语言:javascript复制COPYf5abc88ff7e4 :) SELECT toFloat32('0.1234567890') AS a,toTypeName(a);
SELECT
toFloat32('0.1234567890') AS a,
toTypeName(a)
┌──────────a─┬─toTypeName(toFloat32('0.1234567890'))─┐
│ 0.12345679 │ Float32 │
└────────────┴───────────────────────────────────────┘
1 rows in set. Elapsed: 0.005 sec.
f5abc88ff7e4 :) SELECT toFloat32('0.0123456789') AS a,toTypeName(a);
SELECT
toFloat32('0.0123456789') AS a,
toTypeName(a)
┌───────────a─┬─toTypeName(toFloat32('0.0123456789'))─┐
│ 0.012345679 │ Float32 │
└─────────────┴───────────────────────────────────────┘
1 rows in set. Elapsed: 0.036 sec.
f5abc88ff7e4 :) SELECT toFloat64('0.12345678901234567890') AS a,toTypeName(a);
SELECT
toFloat64('0.12345678901234567890') AS a,
toTypeName(a)
┌───────────────────a─┬─toTypeName(toFloat64('0.12345678901234567890'))─┐
│ 0.12345678901234568 │ Float64 │
└─────────────────────┴─────────────────────────────────────────────────┘
1 rows in set. Elapsed: 0.005 sec.
f5abc88ff7e4 :) SELECT toFloat64('0.01234567890123456789') AS a,toTypeName(a);
SELECT
toFloat64('0.01234567890123456789') AS a,
toTypeName(a)
┌────────────────────a─┬─toTypeName(toFloat64('0.01234567890123456789'))─┐
│ 0.012345678901234568 │ Float64 │
└──────────────────────┴─────────────────────────────────────────────────┘
1 rows in set. Elapsed: 0.005 sec.
特别地,与标准的SQL
相比,ClickHouse
支持如下特殊的浮点数类别:
Inf
- 表示正无穷-Inf
- 表示负无穷NaN
- 表示不是数字
验证一下:
代码语言:javascript复制COPYf5abc88ff7e4 :) SELECT divide(0.5,0);
SELECT 0.5 / 0
┌─divide(0.5, 0)─┐
│ inf │
└────────────────┘
1 rows in set. Elapsed: 0.007 sec.
f5abc88ff7e4 :) SELECT divide(-0.5,0);
SELECT -0.5 / 0
┌─divide(-0.5, 0)─┐
│ -inf │
└─────────────────┘
1 rows in set. Elapsed: 0.004 sec.
f5abc88ff7e4 :) SELECT divide(0.0,0.0);
SELECT 0. / 0.
┌─divide(0., 0.)─┐
│ nan │
└────────────────┘
1 rows in set. Elapsed: 0.005 sec.
高精度数值
高精度数值类型Decimal
一般又称为为定点数,可以指定总位数和固定位数小数点,表示一定范围内的精确数值。Decimal
的原生表示形式为Decimal(P,S)
,两个参数的意义是:
P
:代表精度,决定总位数(也就是决定整数部分加上小数部分一共有多少位数字),取值范围是[1,76]
S
:代表规模(scale
),决定小数位数,取值范围是[0,P]
Decimal(P,S)
衍生出的简单表示形式有:Decimal32(S)
、Decimal64(S)
、Decimal128(S)
和Decimal256(S)
。见下表:
类型 | P的取值范围 | S的取值范围 | 数值范围 |
---|---|---|---|
Decimal(P,S) | [1,76] | [0,P] | (-1*10^(P - S), 1*10^(P - S)) |
Decimal32(S) | [1,9] | [0,P] | (-1*10^(9 - S), 1*10^(9 - S)) |
Decimal64(S) | [10,18] | [0,P] | (-1*10^(18 - S), 1*10^(18 - S)) |
Decimal128(S) | [19,38] | [0,P] | (-1*10^(38 - S), 1*10^(38 - S)) |
Decimal256(S) | [39,76] | [0,P] | (-1*10^(76 - S), 1*10^(76 - S)) |
如果觉得衍生类型不好理解,还是直接使用Decimal(P,S)
就行。它的定义格式如下:
COPYcolumn_name Decimal(P,S)
# 如
amount Decimal(10,2)
对于四则运算,使用两个不同精度的Decimal
数值进行(内置函数)运算,运算结果小数位的规则如下(假设S1
为左值的小数位,S2
为右值的小数位,S
为结果小数位):
- 对于加法和减法,
S = max(S1,S2)
- 对于乘法,
S = S1 S2
- 对于除法,
S = S1
(结果小数位和被除数小数位一致)
COPYf5abc88ff7e4 :) SELECT toDecimal32(2,4) AS x, toDecimal32(2,2) AS y,x y;
SELECT
toDecimal32(2, 4) AS x,
toDecimal32(2, 2) AS y,
x y
┌──────x─┬────y─┬─plus(toDecimal32(2, 4), toDecimal32(2, 2))─┐
│ 2.0000 │ 2.00 │ 4.0000 │
└────────┴──────┴────────────────────────────────────────────┘
1 rows in set. Elapsed: 0.019 sec.
f5abc88ff7e4 :) SELECT toDecimal32(2,4) AS x, toDecimal32(2,5) AS y,y/x
SELECT
toDecimal32(2, 4) AS x,
toDecimal32(2, 5) AS y,
y / x
┌──────x─┬───────y─┬─divide(toDecimal32(2, 5), toDecimal32(2, 4))─┐
│ 2.0000 │ 2.00000 │ 1.00000 │
└────────┴─────────┴──────────────────────────────────────────────┘
1 rows in set. Elapsed: 0.004 sec.
f5abc88ff7e4 :) SELECT toDecimal32(2,4) AS x, toDecimal32(2,4) AS y,y*x
SELECT
toDecimal32(2, 4) AS x,
toDecimal32(2, 4) AS y,
y * x
┌──────x─┬──────y─┬─multiply(toDecimal32(2, 4), toDecimal32(2, 4))─┐
│ 2.0000 │ 2.0000 │ 4.00000000 │
└────────┴────────┴────────────────────────────────────────────────┘
1 rows in set. Elapsed: 0.004 sec.
重点注意:如果从事的是金融领域等追求准确精度的数值存储,不能使用浮点数,而应该考虑使用整型或者定点数,舍入尽可能交由程序规则处理,毕竟数据库是存储数据的工具,不应该承担太多处理数据计算的职能。
布尔值
ClickHouse
中不存在布尔值类型,官方建议使用UInt8
类型,通过值0
或1
表示false
或true
。
字符串类型
字符串类型主要包括:
- 不定长(动态长度)字符串
String
- 固定长度字符串
FixedString(N)
,这里的N
是最大字节数,而不是长度,例如UTF-8
字符占用3
个字节,GBK
字符占用2
个字节 - 特殊字符串
UUID
(存储的是数值,只是形式是字符串)
ClickHouse
中没有编码的概念,字符串可以包含一组任意字节,这些字节按原样存储和输出。这个编码和解码操作推测完全移交给客户端完成。一般情况下,推荐使用UTF-8
编码存储文本类型内容,这样就能在不进行转换的前提下读取和写入数据。
String
String
类型不限制字符串的长度,可以直接替代其他DBMS
的VARCHAR
、BLOB
、CLOB
等字符串类型,相比VARCHAR
这类要考虑预测数据最大长度,显然String
无比便捷。使用Java
语言开发,直接使用String
类型承接即可。String
类型的数据列的定义如下:
COPYcolumn_name String
FixedString
FixedString
类型的数据列的定义如下:
COPYcolumn_name FixedString(N)
FixedString
表示固定长度N
的字符串,这里的N
代表N
个字节(N
bytes),而不是N
个字符或者N
个码点(code point
)。一些使用FixedString
类型的典型场景:
- 二进制表示存储
IP
地址,如使用FixedString(16)
存储IPV6
地址 - 哈希值的二进制表示形式,如
FixedString(16)
存储MD5
的二进制值,FixedString(32)
存储SHA256
的二进制值
当写入FixedString
类型数据的时候:
- 如果数据字节数大于
N
,则会返回一个Too large value for FixedString(N)
的异常 - 如果数据字节数小于
N
,则会使用null
字节填补剩下的部分
官方文档提示查询条件WHERE中如果需要匹配FixedString类型的列,传入的查询参数要自行补尾部的