三 clickhouse基礎入門


 基本語法演示

-- show databases ;
-- create database if not exists test1 ;
-- use test1 ;
-- select currentDatabase() ;
-- drop database test1 ;

1 數據類型

注意在CK中關鍵字嚴格區分大小寫

ck中建表的時候 ,一定指定表引擎!

create table tb_test1(
id Int8 ,
 name String 
)engine=Memory;
┌─name─┬─type───┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id   │ Int8   │              │                    │         │                  │                │
│ name │ String │              │                    │         │                  │                │
└──────┴────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘

1.1 數值類型

1) IntX和UIntX

以前我們常用Tinyint、Smallint、Int和Bigint指代整數的不同取值范圍。而ClickHouse則直接使用Int8、Int16、Int32和Int64指代4種大小的Int類型,其末尾的數字正好表明了占用字節的大小(8位=1字節),

 

 

*ClickHouse支持無符號的整數,使用前綴U表示*

 

 

 

create table test_int(
    id Int8 ,
    age UInt8 ,
    cdId Int32
)engine=Memory ;

2) FloatX

 

 

 

注意: 和我以前的認知是一樣的,這種數據類型在數據特別精准的情況下可能出現數據精度問題!

Select 8.0/0 -->inf 正無窮

Select -8.0/0 -->inf 負無窮

Select 0/0 -->nan 非數字

3) Decimal

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

Decimal128(S)三種,原生方式為Decimal(P,S),其中:

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

 

 

 

在使用兩個不同精度的定點數進行四則運算的時候,它們的小數點 位數S會發生變化。

在進行加法運算時,S取最大值。例如下面的查詢,toDecimal64(2,4)與toDecimal32(2,2)相加后S=4:

在進行減法運算時,S取最大值。例如下面的查詢,toDecimal64(2,4)與toDecimal32(2,2)相減后S=4:

在進行乘法運算時,S取最和。例如下面的查詢,toDecimal64(2,4)與toDecimal32(2,2)相乘后S=4+2:

在進行除法運算時,S取最大值。例如下面的查詢,toDecimal64(2,4)與toDecimal32(2,2)相除后S=4:但是要保證被除數的S大於除數的S,否則會報錯

 

 

 

1.2 字符串類型

符串類型可以細分為String、FixedString和UUID三類。從命名來看仿佛不像是由一款數據庫提供的類型,反而更像是一門編程語言的設計,沒錯CK語法具備編程語言的特征(數據+運算)

1) String

字符串由String定義,長度不限。因此在使用String的時候無須聲明大小。它完全代替了傳統意義上數據庫的Varchar、Text、Clob和Blob等字符類型。String類型不限定字符集,因為它根本就沒有這個概念,所以可以將任意編碼的字符串存入其中。但是為了程序的規范性和可維護性,在同一套程序中應該遵循使用統一的編碼,例如“統一保持UTF-8編碼”就是一種很好的約定。所以在對數據操作的時候我們不在需要區關注編碼和亂碼問題!

2) FixedString

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

create table test_str(
name String ,
job FixedString(4) -- 最長4個字符
)engine=Memory ;

3) UUID

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

 

CREATE TABLE test_uuid
(
    `uid` UUID,
    `name` String
)
ENGINE = Memory ;
DESCRIBE TABLE test_uuid

┌─name─┬─type───┬
│ uid  │ UUID   │
│ name │ String │ 
└──────┴────────┴
insert into test_uuid select generateUUIDv4() , 'zss' ;
select * from test_uuid ;
┌──────────────────────────────────uid─┬─name─┐
│ 47e39e22-d2d6-46fd-8014-7cd3321f4c7b │ zss  │
└──────────────────────────────────────┴──────┘

-------------------------UUID類型的字段默認補位0-----------------------------
insert into test_uuid (name) values('hangge') ;
┌──────────────────────────────────uid─┬─name─┐
│ 47e39e22-d2d6-46fd-8014-7cd3321f4c7b │ zss  │
└──────────────────────────────────────┴──────┘
┌──────────────────────────────────uid─┬─name───┐
│ 00000000-0000-0000-0000-000000000000 │ hangge │
└──────────────────────────────────────┴────────┘

 

1.3 時間類型

1) Date

Date類型不包含具體的時間信息,只精確到天,支持字符串形式寫入:

CREATE TABLE test_date
(
    `id` int,
    `ct` Date
)
ENGINE = Memory ;
DESCRIBE TABLE test_date  ;
┌─name─┬─type──┬
│ id   │ Int32 │
│ ct   │ Date  │
└──────┴───────┴
insert into test_date vlaues(1,'2021-09-11'),(2,now()) ;
select id , ct from test_date ;

┌─id─┬─────────ct─┐
│  1 │ 2021-09-11 │
│  2 │ 2021-05-17 │
└────┴────────────┘

2) DateTime

DateTime類型包含時、分、秒信息,精確到秒,支持字符串形式寫入:

create table testDataTime(ctime DateTime) engine=Memory ;
insert into testDataTime values('2021-12-27 01:11:12'),(now()) ;
select * from testDataTime ;

3)DateTime64

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

 

-- 建表 
CREATE TABLE test_date_time64
(
    `ctime` DateTime64
)
ENGINE = Memory ;
-- 建表
CREATE TABLE test_date_time64_2
(
    `ctime` DateTime64(2)
)
ENGINE = Memory ;
-- 分別插入數據
insert into test_date_time64 values('2021-11-11 11:11:11'),(now()) ;
insert into test_date_time64_2 values('2021-11-11 11:11:11'),(now()) ;
-- 查詢數據
SELECT *
FROM test_date_time64;
┌───────────────────ctime─┐
│ 2021-11-11 11:11:11.000 │
│ 2021-05-17 10:40:51.000 │
└─────────────────────────┘
SELECT 
    *, toTypeName(ctime)
FROM test_date_time64

┌───────────────────ctime─┬─toTypeName(ctime)─┐
│ 2021-11-11 11:11:11.000 │ DateTime64(3)     │
│ 2021-05-17 10:40:51.000 │ DateTime64(3)     │
------------------------------------------------
SELECT 
    *, toTypeName(ctime)
FROM test_date_time64_2

┌──────────────────ctime─┬─toTypeName(ctime)─┐
│ 2021-11-11 11:11:11.00 │ DateTime64(2)     │
│ 2021-05-17 10:41:26.00 │ DateTime64(2)     │
└────────────────────────┴───────────────────┘

1.4 復雜類型

1) Enum

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

create table test_enum(id Int8 , color Enum('red'=1 , 'green'=2 , 'blue'=3)) engine=Memory ;
insert into  test_enum values(1,'red'),(1,'red'),(2,'green');
也可以使用這種方式進行插入數據:
insert into test_enum values(3,3) ;

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

注意: 其實我們可以使用字符串來替代Enum類型來存儲數據,那么為什么是要使用枚舉類型呢?這是出於性能的考慮。因為雖然枚舉定義中的Key屬於String類型,但是在后續對枚舉的所有操作中(包括排序、分組、去重、過濾等),會使用Int類型的Value值 ,提高處理數據的效率!

  • 限制枚舉類型字段的值

  • 底層存儲的是對應的Int類型的數據

  • 可以使用String

2) Array(T)

CK支持數組這種復合數據類型 , 並且數據在操作在今后的數據分析中起到非常便利的效果!數組的定義方式有兩種 : array(T) [e1,e2...] , 我們在這里要求數組中的數據類型是一致的!

數組的定義 
[1,2,3,4,5]
array('a' , 'b' , 'c')
[1,2,3,'hello']   -- 錯誤
create table test_array(
id Int8 ,
hobby Array(String)
)engine=Memory ;
insert into test_array values(1,['eat','drink','la']),(2,array('sleep','palyg','sql'));
┌─id─┬─hobby───────────────────┐
│  1 │ ['eat','drink','la']    │
│  2 │ ['sleep','palyg','sql'] │
└────┴─────────────────────────┘
select id , hobby  , toTypeName(hobby) from test_array ;
┌─id─┬─hobby───────────────────┬─toTypeName(hobby)─┐
│  1 │ ['eat','drink','la']    │ Array(String)     │
│  2 │ ['sleep','palyg','sql'] │ Array(String)     │
└────┴─────────────────────────┴───────────────────┘
 select id , hobby[2]  , toTypeName(hobby) from test_array ; -- 數組的取值 [index]  1-based

 

3) Tuple

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

select tuple(1,'asb',12.23) as x , toTypeName(x) ;
 ┌─x───────────────┬─toTypeName(tuple(1, 'asb', 12.23))─┐
│ (1,'asb',12.23) │ Tuple(UInt8, String, Float64)      │
└─────────────────┴────────────────────────────────────┘
---簡寫形式
SELECT 
    (1, 'asb', 12.23) AS x,
    toTypeName(x)

┌─x───────────────┬─toTypeName(tuple(1, 'asb', 12.23))─┐
│ (1,'asb',12.23) │ Tuple(UInt8, String, Float64)      │
└─────────────────┴────────────────────────────────────┘
注意:建表的時候使用元組的需要制定元組的數據類型
CREATE TABLE test_tuple ( 
c1 Tuple(UInt8, String, Float64) 
) ENGINE = Memory; 
  • (1,2,3,'abc')

  • tuple(1,2,3,'abc')

  • col Tuple(Int8,Int8,String) -- 定義泛型

 

4) Nested

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

create table test_nested(
    uid Int8 ,
    name String ,
    props Nested(
        pid Int8,
        pnames String ,
        pvalues String
    )
)engine = Memory ;
desc test_nested ;
┌─name──────────┬─type──────────┬
│ uid           │ Int8          │
│ name          │ String        │
│ props.pid     │ Array(Int8)   │
│ props.pnames  │ Array(String) │
│ props.pvalues │ Array(String) │
└───────────────┴───────────────┴

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

 

insert into test_nested values(1,'hadoop',[1,2,3],['p1','p2','p3'],['v1','v2','v3']);
-- 行和行之間的屬性的個數可以不一致 ,但是當前行的Nested類型中的數組個數必須一致
insert into test_nested values(2,'spark',[1,2],['p1','p2'],['v1','v2']);
SELECT *
FROM test_nested

┌─uid─┬─name───┬─props.pid─┬─props.pnames─────┬─props.pvalues────┐
│   1 │ hadoop │ [1,2,3]   │ ['p1','p2','p3'] │ ['v1','v2','v3'] │
└─────┴────────┴───────────┴──────────────────┴──────────────────┘
┌─uid─┬─name──┬─props.pid─┬─props.pnames─┬─props.pvalues─┐
│   2 │ spark │ [1,2]     │ ['p1','p2']  │ ['v1','v2']   │
└─────┴───────┴───────────┴──────────────┴───────────────┘
SELECT 
    uid,
    name,
    props.pid,
    props.pnames[1]
FROM test_nested;
┌─uid─┬─name───┬─props.pid─┬─arrayElement(props.pnames, 1)─┐
│   1 │ hadoop │ [1,2,3]   │ p1                            │
└─────┴────────┴───────────┴───────────────────────────────┘
┌─uid─┬─name──┬─props.pid─┬─arrayElement(props.pnames, 1)─┐
│   2 │ spark │ [1,2]     │ p1                            │
└─────┴───────┴───────────┴───────────────────────────────┘

  

create table test_nested(
id Int8 ,
name String  ,
scores Nested(
       seq  UInt8 ,
	   sx Float64 ,
       yy Float64 ,  
	   yw Float64
	   )
)engine = Memory ;

insert into test_nested values (1,'wbb',[1,2,3],[11,12,13],[14,14,11],[77,79,10]);
insert into test_nested values (2,'taoge',[1,2],[99,10],[14,40],[77,11]);
-- 注意 每行中的數組的個數一致  行和行之間可以不一直被

┌─id─┬─name─┬─scores.seq─┬─scores.sx──┬─scores.yy──┬─scores.yw──┐
│  1 │ wbb  │ [1,2,3]    │ [11,12,13] │ [14,14,11] │ [77,79,10] │
└────┴──────┴────────────┴────────────┴────────────┴────────────┘
┌─id─┬─name──┬─scores.seq─┬─scores.sx─┬─scores.yy─┬─scores.yw─┐
│  2 │ taoge │ [1,2]      │ [99,10]   │ [14,40]   │ [77,11]   │
└────┴───────┴────────────┴───────────┴───────────┴───────────┘
SELECT 
    name,
    scores.sx
FROM test_nested;
┌─name─┬─scores.sx──┐
│ wbb  │ [11,12,13] │
└──────┴────────────┘
┌─name──┬─scores.sx─┐
│ taoge │ [99,10]   │
└───────┴───────────┘

 

5) Map 

https://repo.yandex.ru/clickhouse/rpm/testing/x86_64/ 下載新版本的CK安裝包

6) GEO

  • Point

SET allow_experimental_geo_types = 1;
CREATE TABLE geo_point (p Point) ENGINE = Memory();
INSERT INTO geo_point VALUES((10, 10));
SELECT p, toTypeName(p) FROM geo_point;
┌─p───────┬─toTypeName(p)─┐
│ (10,10) │ Point         │
└─────────┴───────────────┘
  • Ring

SET allow_experimental_geo_types = 1;
CREATE TABLE geo_ring (r Ring) ENGINE = Memory();
INSERT INTO geo_ring VALUES([(0, 0), (10, 0), (10, 10), (0, 10)]);
SELECT r, toTypeName(r) FROM geo_ring;

┌─r─────────────────────────────┬─toTypeName(r)─┐
│ [(0,0),(10,0),(10,10),(0,10)] │ Ring          │
└───────────────────────────────┴───────────────┘
  • Polygon

     

SET allow_experimental_geo_types = 1;
CREATE TABLE geo_polygon (pg Polygon) ENGINE = Memory();
INSERT INTO geo_polygon VALUES([[(20, 20), (50, 20), (50, 50), (20, 50)], [(30, 30), (50, 50), (50, 30)]]);
SELECT pg, toTypeName(pg) FROM geo_polygon;
  • MultiPolygon

SET allow_experimental_geo_types = 1;
CREATE TABLE geo_multipolygon (mpg MultiPolygon) ENGINE = Memory();
INSERT INTO geo_multipolygon VALUES([[[(0, 0), (10, 0), (10, 10), (0, 10)]], [[(20, 20), (50, 20), (50, 50), (20, 50)],[(30, 30), (50, 50), (50, 30)]]]);
SELECT mpg, toTypeName(mpg) FROM geo_multipolygon;

7)IPV4

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

(1)出於便捷性的考量,例如IPv4類型支持格式檢查,格式錯誤的IP數據是無法被寫入的,例如:

INSERT INTO IP4_TEST VALUES ('www.51doit.com','192.0.0')

Code: 441. DB::Exception: Invalid IPv4 value.

(2)出於性能的考量,同樣以IPv4為例,IPv4使用UInt32存儲,相比String更加緊湊,占用的空間更小,查詢性能更快。IPv6類型是基於FixedString(16)封裝的,它的使用方法與IPv4別無二致, 在使用Domain類型的時候還有一點需要注意,雖然它從表象上看起來與String一樣,但Domain類型並不是字符串,所以它不支持隱式的自動類型轉換。如果需要返回IP的字符串形式,則需要顯式調用 IPv4NumToString或IPv6NumToString函數進行轉換。

create table test_domain(
id Int8 ,
ip IPv4
)engine=Memory ;
insert  into test_domain values(1,'192.168.133.2') ;
insert  into test_domain values(1,'192.168.133') ; 在插入數據的會進行數據的檢查所以這行數據會報錯
-- Exception on client:
-- Code: 441. DB::Exception: Invalid IPv4 value.
-- Connecting to database doit1 at localhost:9000 as user default.
-- Connected to ClickHouse server version 20.8.3 revision 54438.

  

8) Boolean和Nullable

ck中沒有Boolean類型 ,使用1和0來代表true和false

Nullable 某種數據類型允許為null , 或者是沒有給值的情況下模式是NULL

create table test_null(
id  Int8 ,
 age Int8
)engine = Memory ;
​
create table test_null2(
id  Int8 ,
 age Nullable(Int8)
)engine = Memory ;

2 基本語法

2.1 DDL基礎

  • 建表

目前只有MergeTree、Merge和Distributed這三類表引擎支持 ALTER查詢,所以在進行alter操作的時候注意表的引擎!

注意在建表的時候一般要求指定表的引擎

CREATE TABLE tb_test1
(
    `id` Int8,
    `name` String
)
ENGINE = Memory() ;
-- 只有 MergeTree支持表結構的修改
-- MergeTree一定指定主鍵和排序字段  order by 代表兩個含義
CREATE TABLE test_alter1
(
    `id` Int8,
    `name` String
)
ENGINE = MergeTree() 
order by id ;
-- 查看建表語句  查看引擎類型參數值
show  create table test_alter1 ;
-----------------------------------
CREATE TABLE doit23.test_alter1
(
    `id` Int8,
    `name` String
)
ENGINE = MergeTree()
ORDER BY id
SETTINGS index_granularity = 8192;

 

  • 修改表結構

-- 查看表結構
desc tb_test1 ;
┌─name─┬─type───┬
│ id   │ Int8   │
│ name │ String │
└──────┴────────┴
-- 添加字段
alter table tb_test1 add column age UInt8 ;-- 報錯 , 因為修改的表引擎是內存引擎,不支持表結構的修改 
-- 創建一張MergeTree引擎的表
CREATE TABLE tb_test2
(
    `id` Int8,
    `name` String
)
ENGINE = MergeTree()
ORDER BY id ;
┌─name─┬─type───┬
│ id   │ Int8   │
│ name │ String │
└──────┴────────┴
-- 添加字段 
alter table tb_test2 add column age UInt8 ;
┌─name─┬─type───┬
│ id   │ Int8   │
│ name │ String │
│ age  │ UInt8  │
└──────┴────────┴
alter table tb_test2 add column gender String after name ; 
┌─name───┬─type───┬
│ id     │ Int8   │
│ name   │ String │
│ gender │ String │
│ age    │ UInt8  │
└────────┴────────┴
-- 刪除字段
alter table tb_test2 drop column age ;
-- 修改字段的數據類型 
alter  table  tb_test2 modify column  gender UInt8 default 0 ;
┌─name───┬─type───┬─default_type─┬─default_expression─┬
│ id     │ Int8   │              │                    │
│ name   │ String │              │                    │
│ gender │ UInt8  │ DEFAULT      │ 0                  │
└────────┴────────┴──────────────┴────────────────────┴
-- 作為一個優秀的程序員,表的字段使用注釋一種良好的習慣, 所以建議大家在操作的時候使用注釋來描述字段的意義
-- 修改 / 添加字段的注釋  內部使用的編碼默認是UTF8
alter table tb_test2 comment column name '用戶名' ;
┌─name───┬─type───┬─default_type─┬─default_expression─┬─comment─┬
│ id     │ Int8   │              │                    │         │
│ name   │ String │              │                    │ 用戶名  │
│ gender │ UInt8  │ DEFAULT      │ 0                  │         │
└────────┴────────┴──────────────┴────────────────────┴─────────┴
  • 移動表

在Linux系統中,mv命令的本意是將一個文件從原始位置A移動到目標位置B,但是如果位 置A與位置B相同,則可以變相實現重命名的作用。ClickHouse的RENAME查詢就與之有着異曲同工之妙,RENAME語句的完整語法如下所示:

-- 修改表名 
rename table tb_test1 to t1 ;
-- 修改多張表名
rename table tb_test2 to t2 , t1 to tt1 ;
-- 移動表到另一數據庫中 
rename table t2 to test1.t ;
-- 查看數據庫下的所有的表 
show tables ;
show tables from db_name ;
  • 設置表屬性

-- 設置列的默認值 
create table tb_test3(
   id Int8 ,
    name String comment '用戶名' ,
    role String comment '角色' default 'VIP'
)engine = Log ;
┌─name─┬─type───┬─default_type─┬─default_expression─┬
│ id   │ Int8   │              │                    │
│ name │ String │              │                    │
│ role │ String │ DEFAULT      │ 'VIP'              │
└──────┴────────┴──────────────┴────────────────────┴
insert into tb_test3 (id , name) values(1,'HANGGE') ;
SELECT *
FROM tb_test3 ;
┌─id─┬─name───┬─role─┐
│  1 │ HANGGE │ VIP  │
└────┴────────┴──────┘

2.2 DML基礎

1) 插入數據

INSERT語句支持三種語法范式,三種范式各有不同,可以根據寫入的需求靈活運用。

第一種方式

使用VALUES格式的常規語法

INSERT INTO [db.]table [(c1, c2, c3…)] VALUES (v11, v12, v13…), (v21, v22, v23…), ...

其中,c1、c2、c3是列字段聲明,可省略。VALUES后緊跟的是由元組組成的待寫入數據,通過下標位 與列字段聲明一一對應。數據支持批量聲明寫入,多行數據之間使用逗號分隔

第二種方式
靜態數據: cat user.txt 
1,zss,23,BJ,M
2,lss,33,NJ,M
3,ww,21,SH,F
create table test_load1(
    id UInt8 ,
    name String ,
    age UInt8 ,
    city String ,
    gender String
)engine=Log ;
-- 將數據導入到表中
cat user.txt  | clickhouse-client  -q 'insert into default.test_load1 format CSV' 
clickhouse-client  -q 'insert into default.test_load1 format CSV'  <  user.txt
上面的兩種方式都可以將數據導入到表中  
-- 我們還可以執行數據行屬性的分割符
clickhouse-client --format_csv_delimiter=',' -q 'insert into default.test_load1 format CSV'  <   user.txt 
第三種方式

INSERT INTO [db.]table [(c1, c2, c3…)] SELECT ...

雖然VALUES和SELECT子句的形式都支持聲明表達式或函數,但是表達式和函數會帶來額外的性能開銷,從而導致寫入性能的下降。所以如果追求極致的寫入性能,就應該盡可能避免使用它們。

create table log3 as log2 ;
Insert into log3 select * from log2 ;

ClickHouse內部所有的數據操作都是面向Block數據塊的,所以INSERT查詢最終會將數據轉換為Block數據塊。也正因如此,INSERT語句在單個數據塊的寫入過程中是具有原子性的。在默認的情況下,每個數據塊最多可以寫入1048576行數據(由max_insert_block_size參數控制)。也就是說,如果一條INSERT語句寫入的數據少於max_insert_block_size行,那么這批數據的寫入是具有原子性的,即要么全部成功,要么全部失敗。需要注意的是,只有在ClickHouse服務端處理數據的時候才具有這種原子寫入的特性,例如使用JDBC或者HTTP接口時。因為max_insert_block_size參數在使用CLI命令行或 者INSERT SELECT子句寫入時是不生效的。

2) 更新刪除數據

ClickHouse提供了DELETE和UPDATE的能力,這類操作被稱為Mutation查詢,它可以看作ALTER語句的變種。雖然Mutation能最終實現修改和刪除,但不能完全以通常意義上的UPDATE和DELETE來理解,我們必須清醒地認識到它的不同:首先,Mutation語句是一種“很重”的操作,更適用於批量數據的修改和刪除;其次,它不支持事務,一旦語句被提交執行,就會立刻對現有數據產生影響,無法回滾;最后, Mutation語句的執行是一個異步的后台過程,語句被提交之后就會立即返回。所以這並不代表具體邏輯已經執行完畢,它的具體執行進度需要通過system.mutations系統表查詢。注意數據的修改和刪除操作是使用用MergeTree家族引擎:

只有MergeTree引擎的數據才能修改

刪除分區數據
-- 創建表
create table test_muta(
    id UInt8 ,
    name String ,
    city String
)engine=MergeTree() 
partition  by city 
order by id ;
-- 導入數據 
clickhouse-client  -q 'insert into test_muta format CSV' < data.csv 
-- 刪除分區數據 
alter table test_muta drop  partition 'SH' ;
條件刪除數據
alter table test_muta delete where id=3 ;  -- 一定加條件
條件更新數據
ALTER TABLE [db_name.]table_name UPDATE column1 = expr1 [, ...] WHERE filter_expr 
ALTER TABLE test_ud
UPDATE name = 'my', job = 'teacher' WHERE id = '2' ; 
​
alter table test_muta update name='李思思'  where id=3 ;

但是注意的時候一定指定where條否則會報錯,這種語法的where條件也可以使用子查詢 ;

2.3 分區表操作

目前只有MergeTree系列 的表引擎支持數據分區,分區的基本概念和意義和hive中的意義一樣,這里不過多贅述!

區內排序 , 合並 ,去重

create table test_partition1(
id String , 
ctime DateTime
)engine=MergeTree() 
partition by toYYYYMM(ctime)
order by (id) ;
-- 查看建表語句 
│ CREATE TABLE default.test_partition1
(
    `id` String,
    `ctime` DateTime
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(ctime)
ORDER BY id
SETTINGS index_granularity = 8192 │
-- 插入數據
insert into test_partition1 values(1,now()) ,(2,'2021-06-11 11:12:13') ;
-- 查看數據
SELECT *
FROM test_partition1 ;
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-11 11:12:13 │
└────┴─────────────────────┘
┌─id─┬───────────────ctime─┐
│ 1  │ 2021-05-19 13:38:29 │
└────┴─────────────────────┘
--  查看表中的分區 
ClickHouse內置了許多system系統表,用於查詢自身的狀態信息。 其中parts系統表專門用於查詢數據表的分區信息。
SELECT 
    name,
    table,
    partition
FROM system.parts
WHERE table = 'test_partition1' ;
┌─name─────────┬─table───────────┬─partition─┐
│ 202105_1_1_0 │ test_partition1 │ 202105    │
│ 202106_2_2_0 │ test_partition1 │ 202106    │
└──────────────┴─────────────────┴───────────┘
insert into test_partition1 values(1,now()) ,(2,'2021-06-12 11:12:13') ;
​
┌─name─────────┬─table───────────┬─partition─┐
│ 202105_1_1_0 │ test_partition1 │ 202105    │
│ 202105_3_3_0 │ test_partition1 │ 202105    │
│ 202106_2_2_0 │ test_partition1 │ 202106    │
│ 202106_4_4_0 │ test_partition1 │ 202106    │
└──────────────┴─────────────────┴───────────┘
-- 刪除分區 
alter table test_partition1 drop partition '202105' ;
刪除分區以后 , 分區中的所有的數據全部刪除 
SELECT 
    name,
    table,
    partition
FROM system.parts
WHERE table = 'test_partition1'
┌─name─────────┬─table───────────┬─partition─┐
│ 202106_2_2_0 │ test_partition1 │ 202106    │
│ 202106_4_4_0 │ test_partition1 │ 202106    │
└──────────────┴─────────────────┴───────────┘
SELECT *
FROM test_partition1
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-12 11:12:13 │
└────┴─────────────────────┘
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-11 11:12:13 │
└────┴─────────────────────┘
​
-- 復制分區 
clickHouse支持將A表的分區數據復制到B表,這項特性可以用於快速數據寫入、多表間數據同步和備份等場景,它的完整語法如下:
ALTER TABLE  B  REPLACE PARTITION partition_expr FROM A 
不過需要注意的是,並不是任意數據表之間都能夠相互復制,它們還需要滿足兩個前提 條件:
·兩張表需要擁有相同的分區鍵
·它們的表結構完全相同。
create table test_partition2  as  test_partition1 ;
show  create table test_partition2 ;  -- 查看表2的建表語句 
│ CREATE TABLE default.test_partition2
    (
        `id` String,
        `ctime` DateTime
    )
    ENGINE = MergeTree()
    PARTITION BY toYYYYMM(ctime)
    ORDER BY id
    SETTINGS index_granularity = 8192 │   -- 兩張表的結構完全一致
-- 復制一張表的分區到另一張表中 
SELECT *
FROM test_partition2
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-12 11:12:13 │
└────┴─────────────────────┘
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-11 11:12:13 │
└────┴─────────────────────┘
┌─id─┬───────────────ctime─┐
│ 2  │ 2021-06-21 11:12:13 │
└────┴─────────────────────┘
----------------------------
alter table test_partition2  replace  partition '202106' from  test_partition1
SELECT 
    name,
    table,
    partition
FROM system.parts
WHERE table = 'test_partition2'
┌─name─────────┬─table───────────┬─partition─┐
│ 202106_2_2_0 │ test_partition2 │ 202106    │
│ 202106_3_3_0 │ test_partition2 │ 202106    │
│ 202106_4_4_0 │ test_partition2 │ 202106    │
└──────────────┴─────────────────┴───────────┘
​
-- 重置分區數據 
如果數據表某一列的數據有誤,需要將其重置為初始值,如果設置了默認值那么就是默認值數據,如果沒有設置默認值,系統會給出默認的初始值,此時可以使用下面的語句實現:
ALTER TABLE tb_name CLEAR COLUMN column_name IN PARTITION partition_expr ;
注意: 不能重置主鍵和分區字段
示例: 
alter  table test_rep clear column name in partition '202105' ;
​
-- 卸載分區 
表分區可以通過DETACH語句卸載,分區被卸載后,它的物理數據並沒有刪除,而是被轉移到了當前數據表目錄的detached子目錄下。而裝載分區則是反向操作,它能夠將detached子目錄下的某個分區重新裝載回去。卸載與裝載這一對伴生的操作,常用於分區數據的遷移和備份場景
​
┌─id─┬─name─┬───────────────ctime─┐
│  1 │      │ 2021-05-19 13:59:49 │
│  2 │      │ 2021-05-19 13:59:49 │
└────┴──────┴─────────────────────┘
┌─id─┬─name─┬───────────────ctime─┐
│  3 │ ww   │ 2021-04-11 11:12:13 │
└────┴──────┴─────────────────────┘
alter table test_rep detach partition '202105' ;
┌─id─┬─name─┬───────────────ctime─┐
│  3 │ ww   │ 2021-04-11 11:12:13 │
└────┴──────┴─────────────────────┘
-- 裝載分區
alter table test_rep attach partition '202105' ;
┌─id─┬─name─┬───────────────ctime─┐
│  1 │      │ 2021-05-19 13:59:49 │
│  2 │      │ 2021-05-19 13:59:49 │
└────┴──────┴─────────────────────┘
┌─id─┬─name─┬───────────────ctime─┐
│  3 │ ww   │ 2021-04-11 11:12:13 │
└────┴──────┴─────────────────────┘
-- 記住,一旦分區被移動到了detached子目錄,就代表它已經脫離了ClickHouse的管理,ClickHouse並不會主動清理這些文件。這些分區文件會一直存在,除非我們主動刪除或者使用ATTACH語句重新裝載 

2.4 視圖

1) 普通視圖

ClickHouse擁有普通和物化兩種視圖,其中物化視圖擁有獨立的存儲,而普通視圖只是一層簡單的查詢代理

CREATE VIEW [IF NOT EXISTS] [db_name.]view_name AS SELECT ... 

普通視圖不會存儲任何數據,它只是一層單純的SELECT查詢映射,起着簡化查詢、明晰語義的作用,對查詢性能不會有任何增強。

create view  test3_view as select id , upper(name) , role from tb_test3 ;
┌─name────────────┐
│ tb_test3        │
│ test3_view      │
│ test_partition1 │
│ test_partition2 │
│ test_rep        │
│ tt1             │
└─────────────────┘
drop view test3_view ;   -- 刪除視圖 

2) 物化視圖

物化視圖支持表引擎,數據保存形式由它的表引擎決定,創建物化視圖的完整語法如下所示

create materialized view mv_log engine=Log populate as select * from log ;

物化視圖創建好之后,如果源表被寫入新數據,那么物化視圖也會同步更新。POPULATE修飾符決定了物化視圖的初始化策略:如果使用了POPULATE修飾符,那么在創建視圖的過程中,會連帶將源表中已存在的數據一並導入,如同執行了INTO SELECT 一般;反之,如果不使用POPULATE修飾符,那么物化視圖在創建之后是沒有數據的,它只會同步在此之后被寫入源表的數據。物化視圖目前並不支持同步刪除,如果在源表中刪除了數據,物化視圖的數據仍會保留。

create materialized view test3_view engine = Log populate as select * from tb_test3 ;
-- 建表的時候同步數據 , 當數據更新以后 物化視圖中的數據會同步更新 , 但是當刪除數據以后,物化視圖中的數據不會被刪除
SELECT *
FROM test3_view ;
┌─id─┬─name───┬─role─┐
│  1 │ HANGGE │ VIP  │
│  2 │ BENGE  │ VIP  │
│  3 │ PINGGE │ VIP  │
└────┴────────┴──────┘
-- 向源表中擦混入數據
SELECT *
FROM test3_view
​
┌─id─┬─name──┬─role─┐
│  4 │ TAOGE │ VIP  │
└────┴───────┴──────┘
┌─id─┬─name───┬─role─┐
│  1 │ HANGGE │ VIP  │
│  2 │ BENGE  │ VIP  │
│  3 │ PINGGE │ VIP  │
└────┴────────┴──────┘
-- 刪除源表中的數據 , 物化視圖中的數據 不會變化   ****  
注意: 數據刪除語法只適用於MergeTree引擎的表   基本語法如下
ALTER TABLE db_name.table_name DROP PARTITION '20210601'
ALTER TABLE db_name.table_name DELETE WHERE day = '20210618'
ALTER TABLE <table_name> UPDATE col1 = expr1, ... WHERE <filter>

Show tables ; 其實物化視圖就是一種特殊的表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM