ClickHouse(05)ClickHouse數據類型詳解

ClickHouse屬於分析型資料庫,ClickHouse提供了許多數據類型,它們可以劃分為基礎類型、複合類型和特殊類型。其中基礎類型使ClickHouse具備了描述數據的基本能力,而另外兩種類型則使ClickHouse的數據表達能力更加豐富立體。

基礎類型

基礎類型只有數值、字元串和時間三種類型,沒有Boolean類型,但可以使用整型的0或1替代。

數值類型

數值類型分為整數、浮點數和定點數三類,接下來分別進行說明。

Int

在普遍觀念中,常用Tinyint、Smallint、Int和Bigint指代整數的不同取值範圍。而ClickHouse則直接使用Int8、Int16、Int32和Int64指代4種大小的Int類型,其末尾的數字正好表明了佔用位元組的大小(8位=1位元組)。

ClickHouseInt數據類型
ClickHouse無符號Int數據類型

Float

與整數類似,ClickHouse直接使用Float32和Float64代表單精度浮點數以及雙精度浮點數。在使用浮點數的時候,要意識到它是有限精度的。對Float32和Float64寫入超過有效精度的數值,結果就會出現數據誤差,會被截斷。

ClickHouseFloat數據類型

另外,ClickHousae對於正無窮、負無窮、以及非數值類型的表示。

  • 正無窮:inf
  • 負無窮:-inf
  • 非數值類型:

Decimal

要更高精度的數值運算,需要使用定點數。ClickHouse提供了Decimal32、Decimal64和Decimal128三種精度的定點數。可以通過兩種形式聲明定點:簡寫方式有Decimal32(S)、Decimal64(S)、Decimal128(S)三種,原生方式為Decimal(P,S),其中:

  • P代表精度,決定總位數(整數部分+小數部分),取值範圍是1~38;
  • S代表規模,決定小數位數,取值範圍是0~P。

ClickHouseDecimal數據類型

字元串類型

字元串類型可以細分為String、FixedString和UUID三類。

String

字元串由String定義,長度不限。因此在使用String的時候無須聲明大小。它完全代替了傳統意義上資料庫的Varchar、Text、Clob和Blob等字元類型。String類型不限定字符集,因為它根本就沒有這個概念,所以可以將任意編碼的字元串存入其中。

FixedString

FixedString類型和傳統意義上的Char類型有些類似,對於一些字元有明確長度的場合,可以使用固定長度的字元串。定長字元串通過FixedString(N)聲明,其中N表示字元串長度。但與Char不同的是,FixedString使用null位元組填充末尾字元,而Char通常使用空格填充。比如在下面的例子中,字元串『abc』雖然只有3位,但長度卻是5,因為末尾有2位空字元填充。

UUID

UUID是一種資料庫常見的主鍵類型,在ClickHouse中直接把它作為一種數據類型。UUID共有32位,它的格式為8-4-4-4-12。如果一個UUID類型的欄位在寫入數據時沒有被賦值,則會依照格式使用0填充。

時間類型

時間類型分為DateTime、DateTime64和Date三類。ClickHouse目前沒有時間戳類型。時間類型最高的精度是秒,也就是說,如果需要處理毫秒、微秒等大於秒解析度的時間,則只能藉助UInt類型實現。

DateTime

DateTime類型包含時、分、秒資訊,精確到秒。

DateTime64

DateTime64可以記錄亞秒,它在DateTime之上增加了精度的設置。

Date

Date類型不包含具體的時間資訊,只精確到天。

複合類型

ClickHouse還提供了數組、元組、枚舉和嵌套四類複合類型。

數組Array

數組有兩種定義形式,常規方式array(T),或者簡寫方式[T]。在同一個數組內可以包含多種數據類型,例如數組[1,2.0]是可行的。但各類型之間必須兼容,例如數組[1,’2′]則會報錯。

在查詢時並不需要主動聲明數組的元素類型。因為ClickHouse的數組擁有類型推斷的能力,推斷依據:以最小存儲代價為原則,即使用最小可表達的數據類型。

--常規定義方式
SELECT array(1, 2) as a , toTypeName(a)
┌─a───┬─toTypeName(array(1, 2))─┐
│ [1,2] │ Array(UInt8)              │
└─────┴────────────────┘

--簡寫定義方式
SELECT [1, 2]

--建表時數據類型定義
CREATE TABLE Array_TEST (
    c1 Array(String)
) engine = Memory

元組Tuple

元組類型由1~n個元素組成,每個元素之間允許設置不同的數據類型,且彼此之間不要求兼容。元組同樣支援類型推斷,其推斷依據仍然以最小存儲代價為原則。與數組類似,元組也可以使用兩種方式定義,常規方式tuple(T),或者簡寫方式(T)。

--常規定義方式
SELECT tuple(1,'a',now()) AS x, toTypeName(x)
┌─x─────────────────┬─toTypeName(tuple(1, 'a', now()))─┐
│ (1,'a','2019-08-28 21:36:32') │ Tuple(UInt8, String, DateTime)    │
└───────────────────┴─────────────────────┘

--簡寫定義方式
SELECT (1,'a',now()) AS x, toTypeName(x)
┌─x─────────────────┬─toTypeName(tuple(1, 'a', now()))─┐
│ (1,'a','2019-08-28 21:36:32') │ Tuple(UInt8, String, DateTime)    │
└───────────────────┴─────────────────────┘

--建表時元組類型定義
CREATE TABLE Array_TEST (
    c1 Array(String)
) engine = Memory

枚舉Enum

ClickHouse支援枚舉類型,這是一種在定義常量時經常會使用的數據類型。ClickHouse提供了Enum8和Enum16兩種枚舉類型,它們除了取值範圍不同之外,別無二致。枚舉固定使用(String:Int)Key/Value鍵值對的形式定義數據,所以Enum8和Enum16分別會對應(String:Int8)和(String:Int16)。

在定義枚舉集合的時候,有幾點需要注意。首先,Key和Value是不允許重複的,要保證唯一性。其次,Key和Value的值都不能為Null,但Key允許是空字元串。在寫入枚舉數據的時候,只會用到Key字元串部分。

數據在寫入的過程中,會對照枚舉集合項的內容逐一檢查。如果Key字元串不在集合範圍內則會拋出異常。

為什麼還需要專門的枚舉類型呢?這是出於性能的考慮。因為枚舉定義中的Key屬於String類型,但在後續對枚舉的所有操作中(包括排序、分組、去重、過濾等),會使用Int類型的Value值。

--枚舉類型定義
CREATE TABLE Enum_TEST (
    c1 Enum8('ready' = 1, 'start' = 2, 'success' = 3, 'error' = 4)
) ENGINE = Memory;

--枚舉類型插入
INSERT INTO Enum_TEST VALUES('ready');
INSERT INTO Enum_TEST VALUES('start');

嵌套Nested

嵌套類型,顧名思義是一種嵌套表結構。一張數據表,可以定義任意多個嵌套類型欄位,但每個欄位的嵌套層級只支援一級,即嵌套表內不能繼續使用嵌套類型。對於簡單場景的層級關係或關聯關係,使用嵌套類型也是一種不錯的選擇。

--創建Nested語句
CREATE TABLE nested_test (
    name String,
    age  UInt8 ,
    dept Nested(
        id UInt8,
        name String
    )
) ENGINE = Memory;

ClickHouse的嵌套類型和傳統的嵌套類型不相同,導致在初次接觸它的時候會讓人十分困惑。以上面這張表為例,如果按照它的字面意思來理解,會很容易理解成nested_test與dept是一對一的包含關係,其實這是錯誤的。

嵌套類型本質是一種多維數組的結構。嵌套表中的每個欄位都是一個數組,並且行與行之間數組的長度無須對齊,在同一行數據內每個數組欄位的長度必須相等。

插入數據時候每一個nestd欄位要需要一個數組。

--插入數據
INSERT INTO nested_test VALUES ('bruce' , 30 , [10000,10001,10002], ['研發部','技術支援中心','測試部']);
--行與行之間,數組長度無須對齊
INSERT INTO nested_test VALUES ('bruce' , 30 , [10000,10001], ['研發部','技術支援中心']); 


--查詢數據
SELECT name, dept.id, dept.name FROM nested_test
┌─name─┬─dept.id──┬─dept.name─────────────┐
│ bruce │ [16,17,18] │ ['研發部','技術支援中心','測試部'] │
└────┴───────┴────────────────────┘

特殊數據類型

Nullable

Nullable並不能算是一種獨立的數據類型,它更像是一種輔助的修飾符,需要與基礎數據類型一起搭配使用。Nullable類型與Java8的Optional對象有些相似,它表示某個基礎數據類型可以是Null值。

CREATE TABLE Null_TEST (
    c1 String,
    c2 Nullable(UInt8)
) ENGINE = TinyLog;
--通過Nullable修飾後c2欄位可以被寫入Null值:
INSERT INTO Null_TEST VALUES ('nauu',null)
INSERT INTO Null_TEST VALUES ('bruce',20)
SELECT c1 , c2 ,toTypeName(c2) FROM Null_TEST
┌─c1───┬───c2─┬─toTypeName(c2)─┐
│ nauu   │ NULL    │ Nullable(UInt8) │
│ bruce  │ 20      │ Nullable(UInt8) │
└─────┴──────┴───────────┘

Domain

域名類型分為IPv4和IPv6兩類,本質上它們是對整型和字元串的進一步封裝。IPv4類型是基於UInt32封裝的。

ClickHouse相關資料分享

ClickHouse經典中文文檔分享

參考文章

ClickHouse(05)ClickHouse數據類型詳解