ClickHouse 创建数据库/建表/视图/字典 SQL

Posted 东海陈光剑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClickHouse 创建数据库/建表/视图/字典 SQL相关的知识,希望对你有一定的参考价值。

1.1. ClickHouse SQL之数据定义语言 DDL 

本节介绍 ClickHouse 中进行数据库、表结构的定义和管理。

1.1.1. 概述

在SQL中,数据定义语言( DDL ) 用来创建和修改数据库Schema,例如表、索引和用户等。其中数据库的Schema描述了用户数据模型、字段和数据类型。DDL 语句类似于用于定义数据结构的计算机编程语言。常见DDL 语句包括CREATE、ALTER、DROP等。

ClickHouse SQL中的DDL,除了可以定义数据库、表、索引和视图等之外,还可以定义函数和字典等。

1.1.2. 创建数据库

语法

CREATE DATABASE [IF NOT EXISTS] db_name
[ON CLUSTER cluster]
[ENGINE = db_engine(...)]
[COMMENT 'Comment']

功能说明

1. 创建名称为db_name的数据库。

2. 如果指定了 ON CLUSTER cluster 子句,那么在指定集群 cluster 的所有服务器上创建 db_name 数据库。

3. ENGINE = db_engine(...), 数据库引擎。ClickHouse 默认使用 Atomic 数据库引擎,即有默认值 ENGINE = Atomic。Atomic 引擎提供了可配置的 table engines 和 SQL dialect,它支持非阻塞的DROP TABLE和RENAME TABLE查询和原子的表交换查询命令 EXCHANGE TABLES t1 AND t2。Atomic 中的所有表都有持久的 UUID,数据存储在/clickhouse_path/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/ 路径下。其中,xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy是表 UUID,支持在不更改 UUID 和移动表数据的情况下执行重命名。可以使用DatabaseCatalog,通过 UUID访问 Atomic 数据库中的表。执行DROP TABLE 命令,不会删除任何数据,Atomic 数据库只是通过将元数据移动到 /clickhouse_path/metadata_dropped/,并将表标记为已删除,并通知 DatabaseCatalog。

在 20.5 版本中(可以使用 SELECT version() 查看 ClickHouse 版本),ClickHouse 首次引入了数据库引擎 Atomic。从 20.10 版开始,它是默认数据库引擎(之前默认使用 engine=Ordinary )。SQL 如下:

SELECT version()
 
Query id: 45ca90b4-5e17-4106-a92f-f8fed8822286
 
┌─version()────┐
│ 21.12.1.8808 │
└──────────────┘
 
SHOW CREATE DATABASE system
FORMAT TSVRaw
 
Query id: 19d3e06d-dfaf-45b4-a67d-156199098bc4
 
CREATE DATABASE system
ENGINE = Atomic

这两个数据库引擎在文件系统上存储数据的方式有所不同(Ordinary引擎的文件系统布局更简单),Atomic 引擎解决了Ordinary引擎中存在的一些问题。

Atomic 引擎支持非阻塞删除表/重命名表、异步表删除和分离(delete&detach)(等待查询完成,但对新查询不可见)、原子删除表(删除所有文件/文件夹)、原子表交换(通过“EXCHANGE TABLES t1 AND t2”进行表交换)、重命名字典/重命名数据库、

在FS和ZK 中自动生成唯一UUID 路径用于复制。ClickHouse 支持的数据库引擎可以在 源码目录 src/Databases 下面找到。例如在DatabaseFactory.cpp(112行)中,可以看到 ClickHouse数据库引擎集合:

database_engines "Ordinary", "Atomic", "Memory","Dictionary", "Lazy", "Replicated", "mysql", "MaterializeMySQL", "MaterializedMySQL", "PostgreSQL", "MaterializedPostgreSQL","SQLite"

4. COMMENT 'Comment',添加数据库注释。所有数据库引擎都支持该注释。

Ø 数据库引擎Atomic与Ordinary对比

数据库引擎

Ordinary

Atomic

文件系统布局

简单

复杂

外部工具支持,如 clickhouse-backup等

好/成熟

有限/测试版

一些 DDL 查询(DROP / RENAME)可能要挂起等待很长时间

是的

没有

支持交换 2 张表操作

renamea to a_old,b to a,a_old to b;操作不是原子的,并且 可以在中间突破(虽然机会很低)。

EXCHANGE TABLES t1 AND t2;原子操作,没有中间状态。

使用 zookeeper路径中的 uuid

无法使用。典型的模式是需要创建同一张表的新版本的时候,给zookeeper路径加上版本后缀。

可以在 zookeeper 路径中使用 uuid。可以在 zookeeper 路径中使用 uuid。但是,当扩展集群时,这需要格外小心。Zookeeper 路径更难以映射到真实表。允许对表进行任何类型的操作(重命名、使用相同名称重新创建等)。

物化视图,建议始终使用 TO 语法。

.inner.mv_name, 名字可读。

.inner_id.uuid,名字不可读。

创建一个使用 Atomic引擎的数据库

CREATE DATABASE if not exists clickhouse_tutorial ENGINE = Atomic;

查看数据库列表

使用命令show databases 可以看到数据库列表如下。

SHOW DATABASES
 
Query id: 08c13dfb-0f5c-4aea-815a-68ec95eaa037
 
┌─name────────────────┐
│ INFORMATION_SCHEMA  │
│ clickhouse_tutorial │
│ default             │
│ information_schema  │
│ mydb                │
│ system              │
│ tutorial            │
└─────────────────────┘

如果想要看当前ClickHouse Server 进程实例下更加详细的数据库列表信息,可以使用 select * from system.databases 命令。为了可读性,输出结果整理成表格如下。

name

engine

data_path

metadata_path

uuid

comment

database

INFORMATION_SCHEMA

Memory

./


00000000-0000-0000-0000-000000000000



clickhouse_tutorial

Atomic

./store/

/Users/chenguangjian/store/3c0/3c0b76c0-1dac-4f88-bc0b-76c01dac3f88/

3c0b76c0-1dac-4f88-bc0b-76c01dac3f88



default

Atomic

./store/

/Users/chenguangjian/store/d62/d62015e0-b943-4090-9620-15e0b9432090/

d62015e0-b943-4090-9620-15e0b9432090



information_schema

Memory

./


00000000-0000-0000-0000-000000000000



mydb

Ordinary

./data/mydb/

/Users/chenguangjian/metadata/mydb/

00000000-0000-0000-0000-000000000000

mydb


system

Atomic

./store/

/Users/chenguangjian/store/268/2682f921-c33f-4278-a682-f921c33f9278/

2682f921-c33f-4278-a682-f921c33f9278



tutorial

Atomic

./store/

/Users/chenguangjian/store/d34/d34824fa-e714-43e8-9348-24fae71403e8/

d34824fa-e714-43e8-9348-24fae71403e8



可以看出,使用Atomic引擎的元数据路径上都带有 UUID,而使用Ordinary引擎的路径使用的是数据库名字,Memory引擎则没有元数据存储到磁盘文件上。

可以使用命令show create database system 查看system 数据库建库命令:

CREATE DATABASE system
ENGINE = Atomic

可见,system使用的是Atomic引擎。

1.1.3. 删除数据库

使用 DROP命令删除数据库,SQL实例如下:

drop DATABASE if exists clickhouse_tutorial;

使用 select * from system.databases 查看数据库列表里面,clickhouse_tutorial已经被删掉。

1.1.4. 创建MergeTree表 

语法

ClickHouse 中最强大的表引擎是*MergeTree家族系列,也是使用最多的表引擎。

创建MergeTree表SQL语法如下:

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
    ...
    INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
    INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2,
    ...
    PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
    PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
    [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name=value, ...]

功能说明

1. ON CLUSTER cluster_name,在ClickHouse集群cluster_name上建表。默认情况下,表仅在当前服务器上创建。如果是在ClickHouse集群cluster_name的所有服务节点上建表,使用分布式 DDL 查询子句 ON CLUSTER 实现。

2. ENGINE = table_engine,指定表引擎。可以通过SQL查询system.table_engines表内容,获得ClickHouse支持的表引擎。如下所示。

SELECT
    name,
    supports_ttl
FROM system.table_engines
 
Query id: f219cd4d-dd2b-4490-a9aa-e66f29cec9ac
 
┌─name───────────────────────────────────┬─supports_ttl─┐
│ PostgreSQL                             │            0 │
│ RabbitMQ                               │            0 │
│ Kafka                                  │            0 │
│ S3                                     │            0 │
│ ExecutablePool                         │            0 │
│ MaterializedView                       │            0 │
│ MaterializedPostgreSQL                 │            0 │
│ EmbeddedRocksDB                        │            0 │
│ View                                   │            0 │
│ JDBC                                   │            0 │
│ Join                                   │            0 │
│ ExternalDistributed                    │            0 │
│ Executable                             │            0 │
│ Set                                    │            0 │
│ Dictionary                             │            0 │
│ GenerateRandom                         │            0 │
│ LiveView                               │            0 │
│ MergeTree                              │            1 │
│ Memory                                 │            0 │
│ Buffer                                 │            0 │
│ MongoDB                                │            0 │
│ URL                                    │            0 │
│ ReplicatedVersionedCollapsingMergeTree │            1 │
│ ReplacingMergeTree                     │            1 │
│ ReplicatedSummingMergeTree             │            1 │
│ COSN                                   │            0 │
│ ReplicatedAggregatingMergeTree         │            1 │
│ ReplicatedCollapsingMergeTree          │            1 │
│ File                                   │            0 │
│ ReplicatedGraphiteMergeTree            │            1 │
│ ReplicatedMergeTree                    │            1 │
│ ReplicatedReplacingMergeTree           │            1 │
│ VersionedCollapsingMergeTree           │            1 │
│ SummingMergeTree                       │            1 │
│ Distributed                            │            0 │
│ TinyLog                                │            0 │
│ GraphiteMergeTree                      │            1 │
│ SQLite                                 │            0 │
│ CollapsingMergeTree                    │            1 │
│ Merge                                  │            0 │
│ AggregatingMergeTree                   │            1 │
│ ODBC                                   │            0 │
│ Null                                   │            0 │
│ StripeLog                              │            0 │
│ Log                                    │            0 │
└────────────────────────────────────────┴──────────────┘
45 rows in set. Elapsed: 0.001 sec.

3. (name1 [type1] , ... ),指定表字段名和字段数据类型。

4. NULL|NOT NULL,字段是否可空。

5. DEFAULT,指定字段默认值。字段可以通过表达式 DEFAULT expr指定默认值。如果 INSERT 查询没有指定对应的列,则通过计算对应的表达式来填充。例如:`URLDomain String DEFAULT domain(URL)`。如果未定义默认值的表达式,则默认值将设置为数字为零,字符串为空字符串,数组为空数组,日期为 1970-01-01,DateTime unix时间戳为零,NULL为空。

6. MATERIALIZED,物化字段列。这样的字段不能在 INSERT 语句中指定值插入,因为这样的字段总是通过使用其他字段计算出来的。

7. EPHEMERAL,临时字段列。这样的列不存储在表中,不能被SELECT 查询,但可以在 CREATE 语句的默认值中引用。

8. ALIAS,字段别名。该别名值不会存储在表中,SELECT 查询中使用星号时不会被替换。如果在查询解析期间扩展了别名,则可以在 SELECT 中使用它。

9. INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,指定索引字段和索引粒度。

10. PROJECTION projection_name_1,指定物化投影字段创建一个 Projection,为当前字段Where查询加速。Projection 思想源自 《C-Store: A Column-oriented DBMS》这篇论文,作者是2015年图灵奖获得者、Vertica 之父,Mike Stonebraker。Projection 意指一组列的组合,可以按照与原表不同的排序存储,并且支持聚合函数的查询。相当于传统意义上的物化视图,用空间换时间。它借鉴 MOLAP 预聚合的思想,在数据写入的时候,根据 PROJECTION定义的表达式,计算写入数据的聚合数据同原始数据一并写入。数据查询的过程中,如果查询SQL通过分析可以通过聚合数据得出,直接查询聚合数据减少计算的开销,解决了由于数据量导致的内存问题。Projection主要分为两种:normal与aggregate。相关源码参阅MergeTreeDataWriter.cpp(574 行)MergeTreeDataWriter::TemporaryPart MergeTreeDataWriter::writeProjectionPart()。

11. ORDER BY expr,指定排序字段元组,也即索引列(ClickHouse中的索引列即排序列)。从左到右顺序,建立稀疏索引组合键。合理设计排序键,对查询性能会有很大提升。一般选择查询条件中,筛选条件频繁的列。可以是单一维度,也可以是组合维度的索引。另外,基数特别大的不适合做索引列,如用户标签表的user_id字段。通常筛选后的数据量在百万级别,性能表现较好。

12. PARTITION BY expr,指定分区字段元组。分区字段数据存储到独立的文件夹目录下。

13. SAMPLE BY expr,指定采样字段。

14. compression_codec,指定压缩算法。通用编解码器有NONE(无压缩)、LZ4、LZ4HC、ZSTD。ClickHouse 默认使用 LZ4压缩算法CODEC(LZ4)。专用编解码器有Delta(delta_bytes)、DoubleDelta(适用于时间序列数据)、Gorilla、T64等。另外,使用CODEC('AES-128-GCM-SIV')、CODEC('AES-256-GCM-SIV')等可以加密磁盘上的数据。

15. TTL expr,表数据存活时间表达式。到期数据ClickHouse 会自动清理,对于数据 Part移动或重新压缩,数据的所有行都必须满足“TTL”表达式条件。只能为 MergeTree 系列表指定。TTL 子句不能用于主键列。例如,根据 date字段判断哪些数据到期, TTL表达式为:TTL date + INTERVAL 7 DAY,其中 date 为日期字段。那么,date=20220301的数据将会在 7 天后,也就是 20220308 零点日期被清理。

16. PRIMARY KEY,索引主键。

17. SETTINGS name=value, ...,指定配置项name=value,多个配置项之间用逗号分隔。例如,指定表的索引粒度:SETTINGS index_granularity = 8192。

实例讲解

创建MergeTree表
create table if not exists clickhouse_tutorial.user_tag
(
    user_id      UInt64 DEFAULT 0,
    gender       String DEFAULT 'NA',
    age          String DEFAULT 'NA',
    active_level String DEFAULT 'NA',
    date         Date
) engine = MergeTree()
      order by (user_id, active_level)
      primary key (user_id)
      partition by (date);

使用 show create table clickhouse_tutorial.user_tag 命令查询建表 SQL,执行结果如下:

SHOW CREATE TABLE clickhouse_tutorial.user_tag
FORMAT TSVRaw
 
Query id: 30ec8e45-8ead-419e-b8fc-dbce54b5939c
 
CREATE TABLE clickhouse_tutorial.user_tag
(
    `user_id` UInt64 DEFAULT 0,
    `gender` String DEFAULT 'NA',
    `age` String DEFAULT 'NA',
    `active_level` String DEFAULT 'NA',
    `date` Date
)
ENGINE = MergeTree
PARTITION BY date
PRIMARY KEY user_id
ORDER BY (user_id, active_level)
SETTINGS index_granularity = 8192
 
1 rows in set. Elapsed: 0.001 sec.

其中,index_granularity = 8192是默认的稀疏索引间隔行数。

使用PROJECTION创建MergeTree 表

使用PROJECTION语句建表:

create table if not exists clickhouse_tutorial.user_tag
(
    UserID     UInt64,
    WatchID    UInt64,
    EventTime  DateTime,
    Sex        UInt8,
    Age        UInt8,
    OS         UInt8,
    RegionID   UInt32,
    RequestNum UInt32,
    EventDate  Date,
    PROJECTION pOS(
        SELECT
        groupBitmap(UserID),
        count(1)
        GROUP BY OS
        ),
    PROJECTION pRegionID(
        SELECT count(1),
        groupBitmap(UserID)
        GROUP BY RegionID
        )
) engine = MergeTree()
      order by (WatchID, UserID, EventTime)
      partition by (EventDate);
ALTER TABLE添加PROJECTION

也可以通过 alter table 语句添加PROJECTION 定义:

ALTER TABLE clickhouse_tutorial.user_tag
ADD PROJECTION pRegionID(SELECT count(1),groupBitmap(UserID) GROUP BY RegionID);

插入数据:

INSERT INTO clickhouse_tutorial.user_tag
(UserID,
 WatchID,
 EventTime,
 Sex,
 Age,
 OS,
 RegionID,
 RequestNum,
 EventDate)
select UserID,
       WatchID,
       EventTime,
       Sex,
       Age,
       OS,
       RegionID,
       RequestNum,
       EventDate
from tutorial.hits_v1;

可以看到 ClickHouse 数据库表文件目录下面多了两个文件夹 pOS.proj和 pRegionID.proj:

.
├── checksums.txt
├── columns.txt
├── count.txt
├── data.bin
├── data.mrk3
├── default_compression_codec.txt
├── minmax_EventDate.idx
├── pOS.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── pRegionID.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── partition.dat
└── primary.idx
 
2 directories, 23 files

每个proj文件夹下面的数据文件有checksums.txt、columns.txt、count.txt、data.bin、data.mrk3、default_compression_codec.txt、primary.idx等(Projection Part复用表分区信息),跟一张表的数据文件基本一样(可以看出,Projection本身就是表)。其中,pOS.proj/columns.txt文件内容是:

columns format version: 1
2 columns:
`OS` UInt8
`groupBitmap(UserID)` AggregateFunction(groupBitmap, UInt64)

pRegionID.proj/columns.txt文件内容是:

columns format version: 1
2 columns:
`RegionID` UInt32
`groupBitmap(UserID)` AggregateFunction(groupBitmap, UInt64)
手动触发物化PROJECTION

注意,只有在创建 PROJECTION 之后,再被写入的数据,才会自动物化。对于历史数据,需要手动触发物化,执行如下 SQL:

alter table clickhouse_tutorial.user_tag MATERIALIZE PROJECTION pOS;
alter table clickhouse_tutorial.user_tag MATERIALIZE PROJECTION pRegionID;

MATERIALIZE PROJECTION 是一个异步的 Mutation 操作,可以通过下面的语句查询状态:

SELECT
    table,
    mutation_id,
    command,
    is_done
FROM system.mutations
 
Query id: b50679e1-963f-4800-9366-abc08539ca23
 
┌─table────┬─mutation_id──────┬─command──────────────────────────┬─is_done─┐
│ user_tag │ mutation_127.txt │ MATERIALIZE PROJECTION pOS       │       1 │
│ user_tag │ mutation_128.txt │ MATERIALIZE PROJECTION pRegionID │       1 │
└──────────┴──────────────────┴──────────────────────────────────┴─────────┘
 
2 rows in set. Elapsed: 0.004 sec.

等MATERIALIZE PROJECTION 生成好了之后,分区目录如下:

.
├── Age.bin
├── Age.mrk2
├── EventDate.bin
├── EventDate.mrk2
├── EventTime.bin
├── EventTime.mrk2
├── OS.bin
├── OS.mrk2
├── RegionID.bin
├── RegionID.mrk2
├── RequestNum.bin
├── RequestNum.mrk2
├── Sex.bin
├── Sex.mrk2
├── UserID.bin
├── UserID.mrk2
├── WatchID.bin
├── WatchID.mrk2
├── checksums.txt
├── columns.txt
├── count.txt
├── default_compression_codec.txt
├── minmax_EventDate.idx
├── pOS.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── pRegionID.proj
│   ├── checksums.txt
│   ├── columns.txt
│   ├── count.txt
│   ├── data.bin
│   ├── data.mrk3
│   ├── default_compression_codec.txt
│   └── primary.idx
├── partition.dat
└── primary.idx
 
2 directories, 39 files

看到在原有 MergeTree 的分区下,多了两个pOS.proj和pRegionID.proj子目录,文件存储和 MergeTree 存储格式是一样的,其中,Projection Part复用表分区信息(minmax_EventDate.idx、partition.dat)。Projection写入与原始数据写入过程一致。每一份数据part写入都会基于原始数据Block,结合Projection定义,计算好聚合数据,然后写入对应分区Part文件。

当查询命中某个 PROJECTION 的时候,就会直接用proj子目录中的数据来加速查询。

设置投影优化生效参数

有了PROJECTION 之后,想要查询时投影优化生效,需要设置allow_experimental_projection_optimization 参数开启这项功能:

SET allow_experimental_projection_optimization=1;

查看SQL执行计划:

EXPLAIN actions = 1
SELECT
    RegionID,
    count(1)
FROM clickhouse_tutorial.user_tag
GROUP BY RegionID
 
Query id: d8a96bb5-c7d5-48bd-98a6-dd148618ef5d
 
┌─explain───────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY))                               │
│ Actions: INPUT :: 0 -> RegionID UInt32 : 0                                │
│          INPUT :: 1 -> count() UInt64 : 1                                 │
│ Positions: 0 1                                                            │
│   SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│     ReadFromStorage (MergeTree(with Aggregate projection pRegionID))      │
└───────────────────────────────────────────────────────────────────────────┘
 
6 rows in set. Elapsed: 0.003 sec.

其中,ReadFromStorage (MergeTree(with Aggregate projection pRegionID)) 表示查询命中 Projection。

如果关闭投影优化,设置如下:

SET allow_experimental_projection_optimization=0;

再次查看 SQL 执行计划,可以看到:

EXPLAIN actions = 1
SELECT
    RegionID,
    count(1)
FROM clickhouse_tutorial.user_tag
GROUP BY RegionID
 
Query id: e903857a-4c30-41e6-9a15-082f98dd7cb6
 
┌─explain───────────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY))                                   │
│ Actions: INPUT :: 0 -> RegionID UInt32 : 0                                    │
│          INPUT :: 1 -> count() UInt64 : 1                                     │
│ Positions: 0 1                                                                │
│   Aggregating                                                                 │
│   Keys: RegionID                                                              │
│   Aggregates:                                                                 │
│       count()                                                                 │
│         Function: count() → UInt64                                            │
│         Arguments: none                                                       │
│         Argument positions: none                                              │
│     Expression (Before GROUP BY)                                              │
│     Actions: INPUT :: 0 -> RegionID UInt32 : 0                                │
│     Positions: 0                                                              │
│       SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│         ReadFromMergeTree                                                     │
│         ReadType: Default                                                     │
│         Parts: 32                                                             │
│         Granules: 13007                                                       │
└───────────────────────────────────────────────────────────────────────────────┘
 
19 rows in set. Elapsed: 0.002 sec.

可以看出,就没有命中 Projection。

关于Projection物化投影具体的使用和性能数据,参见“6.7 PROJECTION高性能实践”。

1.1.5. 复制表 

语法

CREATE TABLE [IF NOT EXISTS] [db.]table_name AS [db2.]name2 [ENGINE = engine]

功能说明

使用源表db2.name2结构,创建表db.table_name表,它们具有相同结构。可以为表指定不同的引擎。如果未指定引擎,则使用与 db2.name2 表相同的引擎。

实例讲解

现有表clickhouse_tutorial.user_tag结构如下

CREATE TABLE clickhouse_tutorial.user_tag
(
    `UserID` UInt64,
    `WatchID` UInt64,
    `EventTime` DateTime,
    `Sex` UInt8,
    `Age` UInt8,
    `OS` UInt8,
    `RegionID` UInt32,
    `RequestNum` UInt32,
    `EventDate` Date,
    PROJECTION pOS
    (
        SELECT
            groupBitmap(UserID),
            count(1)
        GROUP BY OS
    ),
    PROJECTION pRegionID
    (
        SELECT
            count(1),
            groupBitmap(UserID)
        GROUP BY RegionID
    )
)
ENGINE = MergeTree
PARTITION BY EventDate
ORDER BY (WatchID, UserID, EventTime)
SETTINGS index_granularity = 8192

我们基于这张表创建一张新表 clickhouse_tutorial.user_tag_new,SQL 如下:

CREATE TABLE clickhouse_tutorial.user_tag_new AS clickhouse_tutorial.user_tag;

执行 show create table clickhouse_tutorial.user_tag_new 命令:

CREATE TABLE clickhouse_tutorial.user_tag_new
(
    `UserID` UInt64,
    `WatchID` UInt64,
    `EventTime` DateTime,
    `Sex` UInt8,
    `Age` UInt8,
    `OS` UInt8,
    `RegionID` UInt32,
    `RequestNum` UInt32,
    `EventDate` Date,
    PROJECTION pOS
    (
        SELECT
            groupBitmap(UserID),
            count(1)
        GROUP BY OS
    ),
    PROJECTION pRegionID
    (
        SELECT
            count(1),
            groupBitmap(UserID)
        GROUP BY RegionID
    )
)
ENGINE = MergeTree
PARTITION BY EventDate
ORDER BY (WatchID, UserID, EventTime)
SETTINGS index_granularity = 8192

可以看出,这两张表结构一模一样。

1.1.6. 从查询语句创建

语法

CREATE TABLE [IF NOT EXISTS] [db.]table_name
[(name1 [type1], name2 [type2], ...)]
ENGINE = engine
AS SELECT ...

功能说明

根据SELECT查询数据结构,创建一个表结构相同的表,并把SELECT的数据插入到表中。

实例讲解

1、为了完整展示整个过程,我们先创建一个数据库tutorial,SQL 如下:

CREATE DATABASE IF NOT EXISTS tutorial;

2、然后创建表tutorial.hits_v1,SQL 如下:

CREATE TABLE tutorial.hits_v1
(
    `WatchID` UInt64,
    `JavaEnable` UInt8,
    `Title` String,
    `GoodEvent` Int16,
    `EventTime` DateTime,
    `EventDate` Date,
    `CounterID` UInt32,
    `ClientIP` UInt32,
    `ClientIP6` FixedString(16),
    `RegionID` UInt32,
    `UserID` UInt64,
    `CounterClass` Int8,
    `OS` UInt8,
    ...
    `Age` UInt8,
    `Sex` UInt8,
     ...
    `ShareURL` String,
    `ShareTitle` String,
    `ParsedParams` Nested(
        Key1 String,
        Key2 String,
        Key3 String,
        Key4 String,
        Key5 String,
        ValueDouble Float64),
    `IslandID` FixedString(16),
    `RequestNum` UInt32,
    `RequestTry` UInt8
)
    ENGINE = MergeTree()
        PARTITION BY toYYYYMM(EventDate)
        ORDER BY (CounterID, EventDate, intHash32(UserID))
        SAMPLE BY intHash32(UserID);

完整 SQL参见:https://clickhouse.com/docs/en/getting-started/tutorial/ 。

3、向tutorial.hits_v1表中插入数据。下载 ClickHouse 官网提供的测试数据包:

https://datasets.clickhouse.com/hits/tsv/hits_v1.tsv.xz

解压成.tsv数据文件,然后通过clickhouse client --query命令行导入数据:

clickhouse client
-h 127.0.0.1 --port 9009
-u default
--password 7Dv7Ib0g  
--query "INSERT INTO tutorial.hits_v1 FORMAT TSV"
--max_insert_block_size=1000000 < hits_v1.tsv

导入完成之后,可以看看表中数据条数:

SELECT count()
FROM tutorial.hits_v1
 
Query id: d2207e4b-0dbb-4cac-b498-e009534dc98c
 
Connecting to 127.0.0.1:9009 as user default.
Connected to ClickHouse server version 22.4.1 revision 54455.
 
┌─count()─┐
│ 8873898 │
└─────────┘
 
1 rows in set. Elapsed: 0.067 sec.

4、现在,我们通过使用 SELECT语句基于表tutorial.hits_v1创建一张新表,并把tutorial.hits_v1表中相应字段的数据,插入到新表中。执行下面的建表并插入数据 SQL:

CREATE TABLE clickhouse_tutorial.hits_v2
(
    WatchID    UInt64,
    UserID     UInt64,
    EventTime  DateTime,
    OS         UInt8,
    RegionID   UInt32,
    RequestNum UInt32,
    EventDate  Date
) ENGINE = MergeTree
      partition by EventDate
      order by (WatchID, UserID, EventTime)
AS
SELECT WatchID,
       UserID,
       EventTime,
       OS,
       RegionID,
       RequestNum,
       EventDate
from tutorial.hits_v1;

5、查看创建结果。执行下面的 SQL:

show create table clickhouse_tutorial.hits_v2;

可以看到hits_v2建表SQL 如下:

CREATE TABLE clickhouse_tutorial.hits_v2
(
    `WatchID` UInt64,
    `UserID` UInt64,
    `EventTime` DateTime,
    `OS` UInt8,
    `RegionID` UInt32,
    `RequestNum` UInt32,
    `EventDate` Date
)
ENGINE = MergeTree
PARTITION BY EventDate
ORDER BY (WatchID, UserID, EventTime)
SETTINGS index_granularity = 8192

然后,hits_v2中也插入了数据:

SELECT count()
FROM clickhouse_tutorial.hits_v2
 
Query id: 4a835757-fca2-4866-bba7-27130a1444ec
 
┌─count()─┐
│ 8873898 │
└─────────┘
 
1 rows in set. Elapsed: 0.003 sec.

1.1.7. 从表函数创建表

语法

CREATE TABLE [IF NOT EXISTS] [db.]table_name AS table_function()

功能说明

创建一个与表函数返回结果相同的表。

实例讲解

ClickHouse 提供了表函数(table function)来构造表。这些表函数如下:

SELECT *
FROM system.table_functions
 
Query id: 3b768f92-72ca-46bf-b4a3-27e993fae21e
 
┌─name───────────────┐
│ dictionary         │
│ numbers_mt         │
│ view               │
│ cosn               │
│ generateRandom     │
│ remote             │
│ input              │
│ s3Cluster          │
│ values             │
│ s3                 │
│ url                │
│ remoteSecure       │
│ sqlite             │
│ zeros              │
│ jdbc               │
│ zeros_mt           │
│ postgresql         │
│ odbc               │
│ executable         │
│ clusterAllReplicas │
│ cluster            │
│ merge              │
│ null               │
│ file               │
│ numbers            │
└────────────────────┘
 
25 rows in set. Elapsed: 0.001 sec.

下面就用实例来说明常用表函数的用法。

file表函数

使用场景

当我们需要把数据从 ClickHouse 导出到文件,或者将数据从一种格式转换为另一种格式,或者通过编辑磁盘上的文件来更新 ClickHouse 中的数据的时候,就可以使用 ClickHouse 文件表引擎(File Table Engine)。

创建文件表引擎

在clickhouse_tutorial数据库中,创建一张使用 File 引擎的表,表名为file_table_demo:

CREATE TABLE clickhouse_tutorial.file_table_demo
(
    `name` String,
    `value` UInt32
)
ENGINE = File(CSV)
 
Query id: 235acc8a-11c5-46ca-a90b-e65797d5ba79
 
Ok.
 
0 rows in set. Elapsed: 0.003 sec.

执行上面的建表 SQL,创建了文件目录:

 /Users/chenguangjian/data/clickhouse_tutorial/file_table_demo

这是一个软连接,指向目录.../store/94a/94a2972d-80c6-4556-94a2-972d80c65556/。

drwxr-x---  4 chenguangjian  staff  128  3 21 22:14 .
drwxr-x---  8 chenguangjian  staff  256  3 21 03:08 ..
lrwxr-x---  1 chenguangjian  staff   64  3 21 22:14 file_table_demo -> /Users/chenguangjian/store/1b1/1b136096-58ae-40e1-9b13-609658ae90e1/
lrwxr-x---  1 chenguangjian  staff   64  3 21 13:06 user_tag -> /Users/chenguangjian/store/94a/94a2972d-80c6-4556-94a2-972d80c65556/
插入数据

手动准备数据文件data.csv,内容如下

$cat data.csv
1,a
2,b
3,c

并把data.csv复制到/Users/chenguangjian/data/clickhouse_tutorial/file_table_demo/ 目录下。这样就完成了表数据的插入。

查询数据
SELECT *
FROM clickhouse_tutorial.file_table_demo
 
Query id: 0ae85ab2-8188-4b32-8f24-00c5b8b4018a
 
┌─name─┬─value─┐
│ a    │     1 │
│ b    │     2 │
│ c    │     3 │
└──────┴───────┘
 
3 rows in set. Elapsed: 0.002 sec.
支持的文件格式

文件表引擎支持TabSeparated、JSON、CSV、Native 等文件格式。完整文件格式清单如下:

SELECT *
FROM system.formats
 
Query id: 0fde0b5f-b625-4f60-b8cf-6156b66f2ca3
 
┌─name───────────────────────────────────────┬─is_input─┬─is_output─┐
│ CapnProto                                  │        1 │         1 │
│ PostgreSQLWire                             │        0 │         1 │
│ MySQLWire                                  │        0 │         1 │
│ JSONStringsEachRowWithProgress             │        0 │         1 │
│ JSONEachRowWithProgress                    │        0 │         1 │
│ JSONCompact                                │        0 │         1 │
│ Null                                       │        0 │         1 │
│ JSONStrings                                │        0 │         1 │
│ JSON                                       │        0 │         1 │
 ...
│ Regexp                                     │        1 │         0 │
│ TSV                                        │        1 │         1 │
│ Vertical                                   │        0 │         1 │
│ CSV                                        │        1 │         1 │
│ TSVRaw                                     │        1 │         1 │
│ Values                                     │        1 │         1 │
│ JSONStringsEachRow                         │        1 │         1 │
│ TabSeparatedWithNamesAndTypes              │        1 │         1 │
│ TSVRawWithNames                            │        1 │         1 │
│ JSONCompactEachRowWithNamesAndTypes        │        1 │         1 │
│ TabSeparatedRaw                            │        1 │         1 │
│ TSVWithNames                               │        1 │         1 │
│ JSONEachRow                                │        1 │         1 │
└────────────────────────────────────────────┴──────────┴───────────┘
 
68 rows in set. Elapsed: 0.002 sec.

numbers(N)表函数

返回具有单个“数字”列 (UInt64) 的表,其中包含从 0 到 N-1 的整数。类似于 system.numbers 表,它可以用于测试和生成连续值。下面 3 个查询是等价的:

SELECT * FROM numbers(10);
SELECT * FROM numbers(0, 10);
SELECT * FROM system.numbers LIMIT 10;

输出:

┌─number─┐
│      0 │
│      1 │
│      2 │
│      3 │
│      4 │
│      5 │
│      6 │
│      7 │
│      8 │
│      9 │
└────────┘
10 rows in set. Elapsed: 0.001 sec.

remote表函数 

连接远程表:

SELECT *
FROM remote('127.0.0.1:9000', clickhouse_tutorial.user_tag)
LIMIT 3

url表函数

语法:url(URL, format, structure)

功能说明:

URL: String 类型,HTTP 或 HTTPS 服务器地址,可以接收 GET 或 POST 请求

format: String 类型,指定数据的格式

structure:String 类型,确定列名和类型。以格式 'UserID UInt64, Name String' 指定表结构。

例如,从返回CSV 格式的 HTTP API获取包含 String 和 UInt32 类型列的表的前 3 行。

SELECT * FROM url('http://127.0.0.1:8888/', CSV, 'column1 String, column2 UInt32')
LIMIT 3;

hdfs表函数   

语法:hdfs(URI, format, structure)

说明:从 HDFS 文件创建表。此表功能类似于url()表函数。

例如,从hdfs://hdfs1:9000/test 查询前 2 行数据:

SELECT *
FROM hdfs('hdfs://hdfs1:9000/test', 'TSV', 'column1 UInt32, column2 UInt32, column3 UInt32')
LIMIT 2

输出:

┌─column1─┬─column2─┬─column3─┐
│       1 │       2 │       3 │
│       3 │       2 │       1 │
└─────────┴─────────┴─────────┘

1.1.8. 创建视图

ClickHouse 视图可分为普通视图(Normal View)、物化视图(Materialized View)、实时视图(Live View)和窗口视图(Window View)等。创建视图使用CREATE VIEW命令。

普通视图

普通视图不存储任何数据,在每次访问时从源头表中读取。换句话说,普通视图只不过是保存的查询。

语法
CREATE [OR REPLACE] VIEW [IF NOT EXISTS]
[db.]table_name
[ON CLUSTER]
AS SELECT ...
功能说明

创建普通视图。从普通视图读取时,该查询会被解析替换成 FROM 子句的子查询。例如,假设您已经创建了一个视图:

CREATE VIEW my_view AS
SELECT ...

然后,执行视图查询:

SELECT a, b, c FROM my_view

此查询完全等同于使用子查询:

SELECT a, b, c FROM (SELECT ...)
实例讲解

我们先创建一张表clickhouse_tutorial.user_tag,然后插入数据,然后根据这张表创建视图 clickhouse_tutorial.user_tag_view。

1创建源头表
CREATE TABLE clickhouse_tutorial.user_tag
(
    `UserID` UInt64,
    `WatchID` UInt64,
    `EventTime` DateTime,
    `Sex` UInt8,
    `Age` UInt8,
    `OS` UInt8,
    `RegionID` UInt32,
    `RequestNum` UInt32,
    `EventDate` Date,
    PROJECTION pOS
    (
        SELECT
            groupBitmap(UserID),
            count(1)
        GROUP BY OS
    ),
    PROJECTION pRegionID
    (
        SELECT
            count(1),
            groupBitmap(UserID)
        GROUP BY RegionID
    )
)
ENGINE = MergeTree
PARTITION BY EventDate
ORDER BY (WatchID, UserID, EventTime)
SETTINGS index_granularity = 8192
2插入数据
INSERT INTO clickhouse_tutorial.user_tag
(UserID,
 WatchID,
 EventTime,
 Sex,
 Age,
 OS,
 RegionID,
 RequestNum,
 EventDate)
select UserID,
       WatchID,
       EventTime,
       Sex,
       Age,
       OS,
       RegionID,
       RequestNum,
       EventDate
from tutorial.hits_v1;
3、创建普通视图

计算源头表clickhouse_tutorial.user_tag中的uniqExact(UserID) 、Age,执行如下 SQL创建普通视图 user_tag_view :

CREATE VIEW clickhouse_tutorial.user_tag_view AS
SELECT
    uniqExact(UserID) AS userCnt,
    Age AS age
FROM clickhouse_tutorial.user_tag
GROUP BY Age

视图看起来与普通表相同。例如,视图也在“SHOW TABLES”查询的结果中。执行如下 SQL 查看视图:

USE clickhouse_tutorial
SHOW TABLES
 
Query id: 94bc8ce6-37e7-489b-9a98-07f0d6cc0d71
 
┌─name──────────┐
│ hits_v2       │
│ user_tag      │
│ user_tag_new  │
│ user_tag_view │
└───────────────┘
 
4 rows in set. Elapsed: 0.002 sec.

可以看到,视图也是作为 Table 在列表里返回。但是,我们去 ClickHouse 存储表数据的文件目录 /Users/data/clickhouse/data/clickhouse_tutorial 查看,并没有 user_tag_view 文件夹:

$ls -la | awk 'print $9 $10 $11'
.
..
hits_v2->/Users/data/clickhouse/store/621/62181267-82f4-438d-9d1b-18437c1a86c0/
user_tag->/Users/data/clickhouse/store/daf/daf4def5-8bdc-4185-9674-3d406a37889a/
user_tag_new->/Users/data/clickhouse/store/d2c/d2c144a3-ed75-4db4-ac50-daab9b659427/

我们先记下user_tag表对应的文件目录 UUID,后面查询视图数据的时候,我们可以通过查看 ClickHouse Server 端的日志,确认查询视图数据,最终走的还是查询视图源表的数据。

4查询视图数据

执行下面的 SQL 查询视图clickhouse_tutorial.user_tag_view里面的数据:

SELECT
    userCnt,
    age
FROM clickhouse_tutorial.user_tag_view
 
Query id: 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6
 
┌─userCnt─┬─age─┐
│   82404 │   0 │
│   21066 │  16 │
│    6556 │  22 │
│    2129 │  26 │
│    6282 │  39 │
│    8311 │  55 │
└─────────┴─────┘
 
6 rows in set. Elapsed: 0.127 sec. Processed 8.87 million rows, 79.87 MB (69.64 million rows/s., 626.72 MB/s.)

我们去 ClickHouse Server 端看一下请求日志,搜索Query id关键字 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 即可搜到:

2022.03.23 03:38:36.268801 [ 2266438 ] <Debug> TCP-Session: 39f75155-4c37-46dc-93ce-95fbabe57f61 Creating query context from session context, user_id: 94309d50-4f52-5250-31bd-74fecac179db, parent context user: default

2022.03.23 03:38:36.268938 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Debug> executeQuery: (from 127.0.0.1:59025) select userCnt,age from clickhouse_tutorial.user_tag_view;

2022.03.23 03:38:36.269120 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Trace> ContextAccess (default): Access granted: SELECT(userCnt, age) ON clickhouse_tutorial.user_tag_view

2022.03.23 03:38:36.269439 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Trace> ContextAccess (default): Access granted: SELECT(UserID, Age) ON clickhouse_tutorial.user_tag

2022.03.23 03:38:36.270055 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Trace> ContextAccess (default): Access granted: SELECT(UserID, Age) ON clickhouse_tutorial.user_tag

2022.03.23 03:38:36.270101 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Trace> InterpreterSelectQuery: FetchColumns -> Complete

2022.03.23 03:38:36.270178 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Trace>

...

2022.03.23 03:38:36.270884 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Debug> clickhouse_tutorial.user_tag (daf4def5-8bdc-4185-9674-3d406a37889a) (SelectExecutor): Selected 27/27 parts by partition key, 27 parts by primary key, 1084/1084 marks by primary key, 1084 marks to read from 27 ranges

2022.03.23 03:38:36.271363 [ 2266438 ] 96bcbd61-0a4d-4d18-9e04-e3896b7e9ad6 <Debug> clickhouse_tutorial.user_tag (daf4def5-8bdc-4185-9674-3d406a37889a) (SelectExecutor): Reading approx. 8873898 rows with 6 streams

...

2022.03.23 03:38:36.395867 [ 2266438 ] <Debug> TCPHandler: Processed in 0.12711 sec.

从 ClickHouse Server 端的查询处理日志可以看出,查询视图 SQL 最终执行的逻辑,还是到源头表clickhouse_tutorial.user_tag (daf4def5-8bdc-4185-9674-3d406a37889a) 文件目录下面去读取相应的数据。

物化视图

物化视图跟普通视图的区别是,物化视图有自己的物理数据文件存储,而普通视图只是一层逻辑查询代理。

语法
CREATE MATERIALIZED VIEW [IF NOT EXISTS]
[db.]mt_table_name
[ON CLUSTER] [TO[db.]name]
[ENGINE = engine]
[POPULATE]
AS SELECT ...
功能说明

1. 创建名称为mt_table_name的物化视图。

2. 物化视图支持表引擎,数据的保存形式由表的引擎决定。

a) 如果想把物化视图数据写入特定物理表数据文件中,可以使用TO [db].[table]。因为物化视图数据文件目录的生成规则是 .inner_id.UUID,可读性比较差。

b) 如果指定TO db.table,不能同时使用 POPULATE,也不需要指定表引擎ENGINE = engine(因为db.table 表结构已经创建好了,表引擎也指定好了)。

c) 如果没有指定TO db.table,则必须指定用于存储物化视图mt_table_name物理数据的表引擎ENGINE = engine。

3. 物化视图在插入目标表期间,使用列名而不是列顺序。如果 SELECT 查询结果中不存在某些列名,使用默认值。SELECT 查询可以包含DISTINCT、GROUP BY、ORDER BY、LIMIT。

4. 创建完物化视图,源表被写入新数据则物化视图也会同步更新。

a) 如果指定POPULATE,则在创建视图时会将现有表数据插入到视图中。

b) 如果不指定POPULATE,SELECT查询结果只包含创建视图后插入表中的数据。

c) 不建议使用 POPULATE,因为在创建物化视图的过程中,同时写入的数据不能被插入物化视图。

5. 建议在使用物化视图时,为每一列添加别名。

6. 物化视图不支持UNION。

Ø Populate 释义

Populate,vt.居住于;生活于;构成…的人口;迁移;移居;殖民于;给文件增添数据,输入数据。

显然,物化视图里关键字POPULATE的意思是“给文件增添数据,输入数据”。

实例讲解
创建物化视图

从源表user_tag创建一个物化视图user_tag_mt_view_1,SQL 如下:

create materialized view clickhouse_tutorial.user_tag_mt_view_1
            engine = MergeTree
                partition by (date)
                order by (age, date)
            populate
as
select uniqExact(UserID) userCnt,
       Age               age,
       EventDate         date
from clickhouse_tutorial.user_tag
group by Age, EventDate;

为了方便测试,我们使用了populate同步源表user_tag中已有的数据到物化视图user_tag_mt_view_1中。

查询物化视图详细信息

创建完成之后,可以到system.tables系统表里查看物化视图表的详细信息。如下:

SELECT *
FROM system.tables
WHERE (database = 'clickhouse_tutorial') AND (name = 'user_tag_mt_view_1')
FORMAT Vertical
 
Query id: 84e4bdd5-5cf2-487b-8f79-512bd766e1dd
 
Row 1:
──────
database:                      clickhouse_tutorial
name:                          user_tag_mt_view_1
uuid:                          1c93ef11-704f-4515-9596-fbdb4efdf6b8
engine:                        MaterializedView
is_temporary:                  0
data_paths:                    ['/Users/data/clickhouse/store/2b4/2b4de01f-12f8-443e-97aa-53ecd1aa49a5/']
metadata_path:                 /Users/data/clickhouse/store/1e0/1e031223-783a-48a9-9f32-1a719033081b/user_tag_mt_view_1.sql
metadata_modification_time:    2022-03-23 15:36:51
dependencies_database:         []
dependencies_table:            []
create_table_query:            CREATE MATERIALIZED VIEW clickhouse_tutorial.user_tag_mt_view_1 (`userCnt` UInt64, `age` UInt8, `date` Date) ENGINE = MergeTree PARTITION BY date ORDER BY (age, date) SETTINGS index_granularity = 8192 AS SELECT uniqExact(UserID) AS userCnt, Age AS age, EventDate AS date FROM clickhouse_tutorial.user_tag GROUP BY Age, EventDate
engine_full:                   MergeTree PARTITION BY date ORDER BY (age, date) SETTINGS index_granularity = 8192
as_select:                     SELECT uniqExact(UserID) AS userCnt, Age AS age, EventDate AS date FROM clickhouse_tutorial.user_tag GROUP BY Age, EventDate
partition_key:                 
sorting_key:                   
primary_key:                   
sampling_key:                  
storage_policy:                
total_rows:                    ᴺᵁᴸᴸ
total_bytes:                   ᴺᵁᴸᴸ
lifetime_rows:                 ᴺᵁᴸᴸ
lifetime_bytes:                ᴺᵁᴸᴸ
comment:                       
has_own_data:                  0
loading_dependencies_database: []
loading_dependencies_table:    []
loading_dependent_database:    []
loading_dependent_table:       []
 
1 rows in set. Elapsed: 0.004 sec.

去 ClickHouse 数据库的数据文件目录 /Users/data/clickhouse/data/clickhouse_tutorial 下面,可以看到多了一个目录:

%2Einner_id%2E1c93ef11%2D704f%2D4515%2D9596%2Dfbdb4efdf6b8 -> /Users/data/clickhouse/store/2b4/2b4de01f-12f8-443e-97aa-53ecd1aa49a5/

上面的%2E,%2D 是转义之后的字符,我们使用 JS脚本函数

decodeURI('%2Einner_id%2E1c93ef11%2D704f%2D4515%2D9596%2Dfbdb4efdf6b8')

解码成可读的字符,就是:

.inner_id.1c93ef11-704f-4515-9596-fbdb4efdf6b8

.inner_id. 后面的这一串,这个正是上面system.tables中查询结果里的uuid: 1c93ef11-704f-4515-9596-fbdb4efdf6b8。物化视图本质上也是一张内置的物理表。关于内置表名的生成规则可以在源代码 StorageMaterializedView.cpp(39行) generateInnerTableName()函数中看到:

static inline String generateInnerTableName(const StorageID & view_id)

    if (view_id.hasUUID())
        return ".inner_id." + toString(view_id.uuid);
    return ".inner." + view_id.getTableName();
查询物化视图数据

执行如下 SQL 查询物化视图数据:

SELECT
    userCnt,
    age,
    date
FROM clickhouse_tutorial.user_tag_mt_view_1
ORDER BY
    age ASC,
    date ASC
 
Query id: 1027e983-2046-442d-aeb2-0ad770adbccc
 
┌─userCnt─┬─age─┬───────date─┐
│   15578 │   0 │ 2014-03-17 │
│   15965 │   0 │ 2014-03-18 │
 ...
│    3339 │  55 │ 2014-03-23 │
└─────────┴─────┴────────────┘
 
42 rows in set. Elapsed: 0.004 sec.
跟源表数据对比

跟源表中的数据对比一下:

SELECT
    uniqExact(UserID) AS userCnt,
    Age AS age,
    EventDate AS date
FROM clickhouse_tutorial.user_tag
GROUP BY
    Age,
    EventDate
ORDER BY
    Age ASC,
    EventDate ASC
 
Query id: 4c700ac2-e5ff-420b-8a8d-5e6f870aa7c9
 
┌─userCnt─┬─age─┬───────date─┐
│   15578 │   0 │ 2014-03-17 │
│   15965 │   0 │ 2014-03-18 │
...
│    3339 │  55 │ 2014-03-23 │
└─────────┴─────┴────────────┘
 
42 rows in set. Elapsed: 0.124 sec. Processed 8.87 million rows, 97.61 MB (71.48 million rows/s., 786.28 MB/s.)

可以看出,物化视图跟源表数据查询结果是一致的。

性能对比

物化视图的核心思想也是预聚合、用空间换时间、数据立方体的思想,主要就是用来提升查询性能的。我们简单对比一下使用物化视图跟使用源表实时查询的性能数据。如下表。

可以看出,物化视图查询性能相对于源表聚合查询,性能上有几十数百倍的提升(由数据量大小、聚合方式、查询聚合字段基数等因素影响)。

指定物理表存储物化视图数据

可以指定用一张物理表来存储物化视图的数据。

1、先创建用来存储物化视图数据的目标表:

create table clickhouse_tutorial.user_tag_mt_view_2_table
(
    date    Date,
    age     UInt8,
    userCnt UInt64
) engine = MergeTree
      partition by (date)
      order by (age, date);

2、然后,创建物化视图,并指定目标表存储物化视图的数据,SQL如下:

create materialized view clickhouse_tutorial.user_tag_mt_view_2
    to clickhouse_tutorial.user_tag_mt_view_2_table
as
select uniqExact(UserID) userCnt,
       Age               age,
       EventDate         date
from clickhouse_tutorial.user_tag
group by Age, EventDate;

此时,指定了 to clickhouse_tutorial.user_tag_mt_view_2_table,不能使用populate。

3、我们去system.tables中看一下user_tag_mt_view_2与user_tag_mt_view_2_table详情信息,执行如下 SQL:

SELECT
    name,
    uuid,
    engine,
    data_paths
FROM system.tables
WHERE (database = 'clickhouse_tutorial') AND (name IN ('user_tag_mt_view_2', 'user_tag_mt_view_2_table'))

输出结果:

name

uuid

engine

data_paths

user_tag_

以上是关于ClickHouse 创建数据库/建表/视图/字典 SQL的主要内容,如果未能解决你的问题,请参考以下文章

大数据clickhouse clickhouse 物化视图使用详解

ClickHouse学习-建表和索引的优化点

ClickHouse学习-建表和索引的优化点

大数据ClickHouse进阶(十六):数据字典创建

大数据ClickHouse进阶(十九):ClickHouse字典的数据源

大数据ClickHouse进阶(二十二):ClickHouse优化

(c)2006-2024 SYSTEM All Rights Reserved IT常识