ClickHouse高级数据查询SQL: WITH/JOIN/IN/INTO OUTFILE/嵌套子查询/交并差计算等
Posted 东海陈光剑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClickHouse高级数据查询SQL: WITH/JOIN/IN/INTO OUTFILE/嵌套子查询/交并差计算等相关的知识,希望对你有一定的参考价值。
本文目录
1.1. SQL概述
1.1.1. SQL简史
1.1.2. SQL概述
1.1.3. ClickHouse SQL
1.1.4. ClickHouse 查询分类
1.2. 数据查询
1.2.1. 概述
1.2.2. WITH子句
1.2.3. FROM子句
1.2.4. SAMPLE子句
1.2.5. JOIN子句
1.2.6. PREWHERE子句
1.2.7. WHERE子句
1.2.8. GROUP BY子句
1.2.9. HAVING子句
1.2.10. SELECT子句
1.2.11. DISTINCT子句
1.2.12. LIMIT子句
1.2.13. SETTINGS子句
1.2.14. UNION子句
1.2.15. INTERSECT子句
1.2.16. EXCEPT子句
1.2.17. INTO OUTFILE子句
1.2.18. FORMAT子句
1.2.19. SHOW 查询
1.2.20. EXISTS 查询
1.2.21. KILL查询
1.1. SQL概述
SQL (Structured Query Language,结构化查询语言) 是一种标准化的声明式编程语言,用于管理关系数据库并对其中的数据执行各种操作。SQL 最初创建于 1970 年代,逐渐成为关系数据库的标准编程语言。SQL 命令分为几种不同的类型,包括:
1. 数据定义语言 ( DDL )。也称为数据定义命令,因为它们用于定义数据表。
2. 数据操作语言 (DML)。用于通过添加、更改或删除数据来操作现有表中的数据。与定义数据存储方式的 DDL 命令不同,DML 命令在使用 DDL 命令定义的表中运行。
3. 数据查询语言(DQL)。仅包含一个命令SELECT,用于从表中获取特定数据。此命令有时与 DML 命令组合在一起。
4. 数据控制语言(DCL)。用于授予或撤销用户访问权限。事务控制语言命令用于更改某些数据的状态。例如,COMMIT事务更改或ROLLBACK事务更改。
本文主要介绍 ClickHouse SQL 相关内容。
SQL 本质上是一种声明式编程语言(4GL),包括数据查询语言(DQL,data query language)、数据操作语言(DML,data manipulation language )(插入、更新和删除)、数据定义语言(DDL, data definition language)(模式创建和修改)和数据访问控制语言(DCL,data control language)等。本节简要介绍 SQL 历史以及ClickHouse SQL特性。
1.1.1. SQL简史
SQL最初基于关系代数(relational algebra)和元组关系演算(tuple relational calculus),由多种类型的语句(statement)组成。SQL 是计算机科学领域历史上的关键里程碑,是计算历史上最成功的想法之一。
追溯到 1970 年代初,SQL 发展简史如下。
l 1970. EF Codd 的“大型共享数据库的数据关系模型”发表在Communications of the ACM上,为 RDBMS 奠定了基础。
l 1974. IBM 研究人员发表了一篇介绍结构化查询语言的文章,最初称为 SEQUEL,因为商标问题,改为 SQL。
l 1977. Relational Software Inc.(后来改名叫:Oracle,甲骨文公司)开始构建商业 RDBMS。
l 1979.甲骨文为 Digital Equipment Corp.的小型计算机系统提供了第一个商用 RDBMS。
l 1982. IBM 发布了 SQL/Data System,这是一种用于 IBM 大型机的 SQL RDBMS。
l 1985. IBM 发布了 Database 2,这是一种用于 IBM 的多虚拟存储大型机操作系统的 SQL RDBMS。
l 1986. ANSI 委员会和 ISO采用 SQL 作为标准。
l 1989. ISO SQL 标准的第一个修订版 SQL-89 发布。
l 1992. ISQ SQL 标准的第一个主要修订版 SQL-92 发布。
l 1999.第一个按照 ISO 命名标准命名的版本 ISO/IEC SQL:1999,增加了编程功能和对 Java的支持。
l 2003. ISO/IEC SQL:2003 增加了对可扩展标记语言 (XML) 对象的预定义数据类型的支持。
l 2006. ISO/IEC SQL:2006 扩展了与 XML 相关的功能。
l 2008. ISO/IEC SQL:2008 增加了对分区 JOIN 的支持,这是一种连接两个或多个表的方法,将连接的表视为单个表。
l 2011. ISO/IEC SQL:2011 改进了对包含时间相关数据的关系数据库的支持。
l 2016. ISO/IEC SQL:2016 添加了可选的新功能,包括JSON特性修订、对多态表函数和行模式匹配的支持等。
l 2019. ISO/IEC SQL:2019 多维数组(SQL/MDA)。制定了SQL关于多维数组类型 (MD Array) 操作功能支持。
1.1.2. SQL概述
众所周知,SQL结构化查询语言是一种数据库语言,我们使用SQL对现有的数据库执行某些操作,也可以使用 SQL来创建数据库、创建表。
SQL简介
SQL使用命令来执行任务。这些SQL命令主要分为四类:
l DDL(Data Definition Language):数据定义语言
l DQL(Data Query Language):数据查询语言
l DML(Data Manipulation Language):数据操作语言
l DCL(Data Control Language):数据控制语言
有时候,也会提到TCL(Transaction Control Language)事务控制语言。
SQL指令集如下图:
DDL数据定义语言
DDL数据定义语言,实际上由可用于定义数据库模式的 SQL 命令组成。它只处理数据库模式的描述,并用于创建和修改数据库中的数据库对象的结构。DDL 是一组用于创建、修改和删除数据库结构而不是数据的 SQL 命令。这些命令通常不被一般用户使用,他们应该通过应用程序访问数据库。
DDL 命令如下:
1. CREATE:此命令用于创建数据库或其对象(如表、索引、函数、视图、存储过程和触发器)。
2. DROP:此命令用于从数据库中删除对象。
3. ALTER:这用于更改数据库的结构。
4. TRUNCATE:这用于从表中删除所有记录,包括删除为记录分配的所有空间。
5. COMMENT:用于向数据字典添加注释。
6. RENAME:这用于重命名数据库中存在的对象。
DQL数据查询语言
DQL语句用于对模式对象中的数据执行查询。DQL 命令的目的是根据传递给它的查询获取一些模式关系。我们可以将 DQL 定义如下,它是 SQL 语句的一个组件,允许从数据库中获取数据并对其进行排序。它包括 SELECT 语句。此命令允许从数据库中获取数据以对其执行操作。当对一个或多个表触发 SELECT 时,结果将编译到另一个临时表中,该表显示或可能由程序(即前端)接收。
DQL 命令如下:
1. SELECT:用于从数据库中检索数据。
DML数据操作语言
DML处理数据库中存在的数据。操作的 SQL 命令属于 DML 或数据操作语言,这包括大多数 SQL 语句。它是控制对数据和数据库的访问的 SQL 语句的组件。基本上,DCL 语句与 DML 语句组合在一起。DML命令如下:
1. INSERT:用于向表中插入数据。
2. UPDATE:它用于更新表中的现有数据。
3. DELETE:用于从数据库表中删除记录。
4. LOCK:表控制并发。
5. CALL:调用 PL/SQL 或 JAVA 子程序。
6. EXPLAIN PLAN:描述数据的访问路径。
DCL数据控制语言
DCL包括GRANT、REVOKE等命令,主要处理数据库系统的权限、权限等控制。
DCL 命令如下:
1. GRANT:此命令 授予用户对数据库的访问权限。
2. REVOKE: 此命令撤销用户使用 GRANT 命令赋予的访问权限。
TCL事务控制语言
还有另一类 SQL 子句:TCL,事务控制语言。TCL 命令主要处理数据库内的事务。
TCL 命令如下:
1. COMMIT:提交事务。
2. ROLLBACK:在发生任何错误的情况下回滚事务。
3. SAVEPOINT:在事务中设置保存点。
4. SET TRANSACTION: 指定交易的特征。
Ø 关系代数与关系演算
ClickHouse 是 ROLAP 型分析数据库。E.F.Codd于1970年起连续发表多篇论文,系统而严格地提出关系模型的概念,奠定了关系数据库的理论基础。关系数据库是目前各类数据库中最重要、最流行的数据库。关系数据库是指支持关系模型的数据库系统。
关系模型
关系模型(Relational Moodel)由关系数据结构、关系操作集合和完整性约束三部分组成。
1. 关系数据结构,是指关系模型中数据的组织方式,具体地来说是一张扁平的二维表,这种简单的结构能够描述出现实世界的实体及实体之间的联系。
2. 关系操作,采用集合操作方式,其对象和结果都是集合。在关系模型中常见的关系操作包括:选择、投影、连接、除、并、交、差等查询类操作和增、删、改等更新类操作。
3. 完整性约束,有实体完整性(Entity Integrity)约束(主键约束、自增约束、唯一约束等)、域完整性(Domain Integrity)约束(检查约束、外键约束、默认值约束、非空约束等)、参照完整性(Referential Integrity)约束和用户自定义完整性(User-defined Integrity)约束等。实体完整性用于保证数据库中数据表的每一个特定实体的记录都是唯一的。域完整性保证指定列的数据具有正确的数据类型、格式和有效的数据范围。其中实体完整性和参照完整性是关系模型必须满足的完整性约束条件。数据完整性用于保证数据库中数据的正确性、一致性和可靠性。
关系模型中的关系操作能力早期通常用代数方式或逻辑方式来表示,分别称为关系代数和关系演算。
关系代数用对关系的运算来表达查询要求,关系演算是用谓词来表达查询要求。
关系代数
关系代数(Relational Algebra)是以集合为对象的操作思维,由集合到集合的变换。关系代数是一种过程查询语言(procedural language),它将关系实例作为输入,并产生关系实例作为输出。它使用运算符来执行查询。运算符可以是一元或二元。他们接受关系作为他们的输入,并让关系作为他们的输出。关系代数在关系上递归执行,中间结果也被认为是关系。
关系代数中包含如下操作:
1. Select (σ):选择操作
2. Project (Π):投影操作
3. Union (U):交集
4. Set Difference (-):差集
5. Cartesian product (X):笛卡尔积
6. Rename (ρ) :重命名
关系演算
关系演算(Relational Calculus)以数理逻辑中的谓词演算为基础,是描述关系运算的另一种思维方式,是一种声明性语言(declarative language)。按照谓词变元的基本对象是元组变量还是域变量,关系演算又可以分为元组关系演算和域关系演算。无论是关系代数、元组关系演算还是域关系演算,三者在表达能力上是完全等价的。一个典型的关系演算表达式如下:
t | P(t)
其中,
t: 元组的集合the set of tuples,
P: 谓词条件,即给定元组集的真值条件。
实际的DBMS除了提供关系代数或关系演算功能外,还提供了许多附加功能,比如聚合函数、关系赋值、算术运算等。
SQL与关系代数、关系演算
SQL(Structured Query Language)结构化查询语言,则继承了关系代数和关系演算各自的优点。
例如,SQL包含了关系演算中的声明性语言特性、基于元组关系演算特性等;SQL中也包含了关系代数中的关系操作集合思想、运算符可以是一元或二元、指定操作顺序、集合交并差运算思想等。
SQL不仅具有丰富的数据查询功能,而且具有数据定义和数据控制功能,是集数据查询、DDL(数据定义语言),DML(数据操纵语言),DCL(数据控制语言)于一体的关系数据语言,是关系数据库的标准语言。
1.1.3. ClickHouse SQL
ClickHouse 支持类 SQL 语言,并提供了丰富的扩展功能。ClickHouse不支持事务。
ClickHouse 提供了多种表引擎,例如MergeTree系列引擎、MaterializedView引擎、Dictionary引擎、Distributed引擎等表引擎。
ClickHouse 还提供了丰富的数据类型和函数,例如数组、Map和嵌套数据结构、近似计算、bitmap 、高阶函数和 URI 函数等。
可以使用 SQL 语句:
select * from system.table_engines
查看 ClickHouse 支持的表引擎
使用 SQL语句:
select * from system.functions
查看 ClickHouse 的内置函数。
ClickHouse 使用递归下降解析器(Recursive Descent Parser)来解析 SQL 查询语句,生成 AST(Abstract Syntax Tree,抽象语法树)。
1.1.4. ClickHouse 查询分类
ClickHouse将查询分为两大类:
第一类是有结果输出的查询,可以在源代码文件ParserQueryWithOutput.cpp中看到:ShowTablesQuery、SelectWithUnionQuery、TablePropertiesQuery、DescribeTableQuery、ShowProcesslistQuery、CreateQuery、AlterQuery、RenameQuery、DropQuery、CheckQuery、OptimizeQuery、KillQueryQuery、WatchQuery、ShowAccessQuery、ShowAccessEntitiesQuery、ShowCreateAccessEntityQuery、ShowGrantsQuery、ShowPrivilegesQuery、ExplainQuery等,对应SHOW、SELECT、CREATE等语句。
第二类是无结果输出的查询,可以在源代码ParserQuery.cpp中看到:InsertQuery、UseQuery、SetQuery、SystemQuery、CreateUserQuery、CreateRoleQuery、CreateQuotaQuery、CreateRowPolicyQuery、CreateSettingsProfileQuery、CreateFunctionQuery、DropFunctionQuery、DropAccessEntityQuery、GrantQuery、SetRoleQuery、ExternalDDLQuery、BackupQuery等,对应INSERT、USE、SET以及EXIT等系统相关语句。
例如,SELECT 查询解析器(ParserSelectQuery.cpp)中就定义了ClickHouse SQL 关键字,相关代码片段如下:
bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
auto select_query = std::make_shared<ASTSelectQuery>();
node = select_query;
ParserKeyword s_select("SELECT");
ParserKeyword s_all("ALL");
ParserKeyword s_distinct("DISTINCT");
ParserKeyword s_distinct_on("DISTINCT ON");
ParserKeyword s_from("FROM");
ParserKeyword s_prewhere("PREWHERE");
ParserKeyword s_where("WHERE");
ParserKeyword s_group_by("GROUP BY");
ParserKeyword s_with("WITH");
ParserKeyword s_totals("TOTALS");
ParserKeyword s_having("HAVING");
ParserKeyword s_window("WINDOW");
ParserKeyword s_order_by("ORDER BY");
ParserKeyword s_limit("LIMIT");
ParserKeyword s_settings("SETTINGS");
ParserKeyword s_by("BY");
ParserKeyword s_rollup("ROLLUP");
ParserKeyword s_cube("CUBE");
ParserKeyword s_top("TOP");
ParserKeyword s_with_ties("WITH TIES");
ParserKeyword s_offset("OFFSET");
ParserKeyword s_fetch("FETCH");
ParserKeyword s_only("ONLY");
ParserKeyword s_row("ROW");
ParserKeyword s_rows("ROWS");
ParserKeyword s_first("FIRST");
ParserKeyword s_next("NEXT");
...
select_query->setExpression(ASTSelectQuery::Expression::WITH, std::move(with_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::TABLES, std::move(tables));
select_query->setExpression(ASTSelectQuery::Expression::PREWHERE, std::move(prewhere_expression));
select_query->setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_expression));
select_query->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, std::move(window_list));
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY, std::move(limit_by_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_OFFSET, std::move(limit_offset));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_LENGTH, std::move(limit_length));
select_query->setExpression(ASTSelectQuery::Expression::SETTINGS, std::move(settings));
return true;
接下来,我们就详细介绍 ClickHouse SQL 查询。
1.1. 数据查询
数据查询语言( DQL ) 是SQL语言子集,也是 SQL中最核心最常用的功能。
1.1.1. 概述
SQL 的核心是 Query,也就是数据查询(Date Query)。SQL中数据查询语句也只有一个,那就是 SELECT语句。SELECT 语句执行数据查询,并把请求响应的数据返回到客户端,返回数据集称为结果集。
类似 Hive SQL,ClickHouse SQL也支持组合使用INSERT INTO ... SELECT语句,将查询结果数据写入到另外一张表里。
SELECT语句
SELECT 语句的语法如下:
[WITH expr_list|(subquery)]
SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
[FROM [db.]table | (subquery) | table_function] [FINAL]
[SAMPLE sample_coeff]
[ARRAY JOIN ...]
[GLOBAL] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI]
JOIN (subquery)|table (ON <expr_list>)|(USING <column_list>)
[PREWHERE expr]
[WHERE expr]
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
[HAVING expr]
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr]
[LIMIT [offset_value, ]n BY columns]
[LIMIT [n, ]m] [WITH TIES]
[SETTINGS ...]
[UNION ...]
[INTO OUTFILE filename [COMPRESSION type] ]
[FORMAT format]
除了SELECT expr_list表达式是必须的,其他有子句都是可选的。SELECT子句是必选的,其它子句如WHERE子句、GROUP BY子句等是可选的。一个SELECT查询语句中,子句的顺序是固定的。必须严格按照上述的顺序书写。
所有的查询语句都是从FROM开始执行的(如果使用 WITH,则先计算 WITH 子句)。在SQL 查询执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。
嵌套子查询
查询可以嵌套,一个查询的结果可以通过关系运算符或聚合函数,在另一个查询中使用。嵌套查询(nested query)也称为子查询(subquery)。例如,下面是一个子查询的SQL实例:
SELECT isbn ,
title ,
price
FROM Book
WHERE price < ( SELECT AVG ( price ) FROM Book )
ORDER BY title ;
查询子句执行顺序
SQL中各个子句的执行顺序如下:
(8) SELECT
(9) DISTINCT<Select_list>
(1) FROM <left_table>
(3) <join_type>JOIN<right_table>
(2) ON<join_condition>
(4) WHERE<where_condition>
(5) GROUP BY<group_by_list>
(6) WITH CUBE|ROLLUP
(7) HAVING <having_condtion>
(10) ORDER BY <order_by_list>
(11) LIMIT <limit_number>
在ClickHouse SQL中,查询子句要丰富一些。ClickHouse SQL中的各个子句执行顺序如下:
① WITH子句
② FROM子句
③ SAMPLE子句
④ JOIN子句
⑤ PREWHERE子句
⑥ WHERE子句
⑦ GROUP BY子句
⑧ LIMIT BY子句
⑨ HAVING子句
⑩ SELECT子句
⑪ DISTINCT子句
⑫ ORDER BY子句
⑬ LIMIT子句
⑭ SETTINGS子句
⑮ UNION子句
⑯ INTERSECT子句
⑰ EXCEPT子句
⑱ INTO OUTFILE子句
⑲ FORMAT子句
下面对这些SQL子句,分别进行详细介绍。
1.1.2. WITH子句
ClickHouse 支持公共表表达式(CTE, Common Table Expressions),即提供在 SELECT查询中使用 WITH 子句的结果。在查询表对象的地方,可以在子查询上下文中,使用命名子查询。另外,ClickHouse 对 CTE 是有限支持,例如:WITH 子句不支持递归查询。当使用子查询时,它的结果应该是只有一行的标量。
语法
WITH <expression> AS <identifier>
功能说明
使用表达式expression声明一个变量名为identifier的命名子查询。
实例讲解
使用常量表达式作为“变量”
WITH '2014-03-18 12:00:00' AS ts_upper_bound
SELECT count()
FROM clickhouse_tutorial.user_tag
WHERE (EventDate = toDate(ts_upper_bound)) AND (EventTime <= ts_upper_bound)
Query id: e37ccab0-3b66-4de2-845f-d0d3682e69d3
┌─count()─┐
│ 678375 │
└─────────┘
1 rows in set. Elapsed: 0.009 sec. Processed 1.38 million rows, 8.30 MB (160.29 million rows/s., 961.76 MB/s.)
使用标量子查询结果
WITH (
SELECT max(EventTime)
FROM clickhouse_tutorial.user_tag
) AS ts_upper_bound
SELECT count()
FROM clickhouse_tutorial.user_tag
WHERE (EventDate = toDate(ts_upper_bound)) AND (EventTime <= ts_upper_bound)
Query id: 7aced70c-0708-467f-8f23-92797343067f
┌─count()─┐
│ 1046491 │
└─────────┘
1 rows in set. Elapsed: 0.023 sec. Processed 9.92 million rows, 41.77 MB (434.91 million rows/s., 1.83 GB/s.)
Ø 公共表表达式(CTE)
在SQL:1999标准中,SQL 中的分层和递归查询是通过递归公用表表达式(CTE) 实现的。递归 CTE 可用于遍历关系(如图形或树)。例如,计算从 0 到 9 的数字的阶乘的递归查询示例如下:
WITH RECURSIVE temp (n, fact) AS
(SELECT 0, 1 -- Initial Subquery
UNION ALL
SELECT n+1, (n+1)*fact FROM temp -- Recursive Subquery
WHERE n < 9)
SELECT * FROM temp;
1.1.3. FROM子句
语法
FROM db.table | (subquery) | table_function] [FINAL]
功能说明
FROM 子句指定从中读取查询的数据源,可以是表(Table)、子查询(Subquery)、表函数(Table function)等。
1. 子查询是另一个 SELECT 查询,可以在 FROM 子句内的括号中指定。
2. FROM 子句可以包含多个数据源,以逗号分隔,相当于对它们进行 CROSS JOIN。
3. JOIN 和 ARRAY JOIN 子句也可用于扩展 FROM 子句的功能。
4. FINAL 不建议使用,此处不作介绍了。
实例讲解
从表中查询
SELECT
UserID,
WatchID
FROM clickhouse_tutorial.user_tag
LIMIT 3
Query id: 05645ee2-401f-4021-818e-f18fcc3481af
┌──────────────UserID─┬─────────────WatchID─┐
│ 2042690798151930621 │ 4611692230555590277 │
│ 1266642432311731534 │ 4611720374393200609 │
│ 1080523977390906965 │ 4611724822782888722 │
└─────────────────────┴─────────────────────┘
3 rows in set. Elapsed: 0.005 sec.
从子查询中查询
SELECT t.WatchID
FROM
(
SELECT
UserID,
WatchID
FROM clickhouse_tutorial.user_tag
LIMIT 3
) AS t
WHERE t.UserID = 2042690798151930621
Query id: e55fa453-f212-49e2-84b3-541ab9dbd5f2
┌─────────────WatchID─┐
│ 4611692230555590277 │
└─────────────────────┘
1 rows in set. Elapsed: 0.008 sec.
从表函数中查询
SELECT *
FROM numbers(10)
Query id: 73c856b4-608d-43f0-882d-a0d8ae01dc49
┌─number─┐
│ 0 │
│ 1 │
│ 2 │
│ 3 │
│ 4 │
│ 5 │
│ 6 │
│ 7 │
│ 8 │
│ 9 │
└────────┘
10 rows in set. Elapsed: 0.003 sec.
结合ARRAY JOIN子句查询
SELECT
t.array1,
t.array2,
x,
y
FROM
(
SELECT
[1, 2, 3] AS array1,
[4, 5, 6] AS array2
) AS t
ARRAY JOIN
array1 AS x,
array2 AS y
Query id: 68b8eab6-242f-4334-aade-a797926cd9ef
┌─array1──┬─array2──┬─x─┬─y─┐
│ [1,2,3] │ [4,5,6] │ 1 │ 4 │
│ [1,2,3] │ [4,5,6] │ 2 │ 5 │
│ [1,2,3] │ [4,5,6] │ 3 │ 6 │
└─────────┴─────────┴───┴───┘
1.1.4. SAMPLE子句
语法
SAMPLE sample_coeff
功能说明
根据采样字段,按照采样率sample_coeff进行采样查询。采样字段适合均匀分布的字段,例如 UserID。
实例讲解
给 user_tag 表添加采样字段:
alter table clickhouse_tutorial.user_tag
modify sample by UserID;
分别按照采样率 0.1、0.01、0.001 进行avg() 的聚合计算:
SELECT
t.c1,
t.c2,
t.c3,
t.c4,
t.c1 - t.c2,
t.c1 - t.c3,
t.c1 - t.c4
FROM
(
SELECT
(
SELECT avg(user_tag.RequestNum) AS req
FROM clickhouse_tutorial.user_tag
WHERE EventDate = '2014-03-18'
) AS c1,
(
SELECT avg(user_tag.RequestNum) AS req
FROM clickhouse_tutorial.user_tag
SAMPLE 1 / 10
WHERE EventDate = '2014-03-18'
) AS c2,
(
SELECT avg(user_tag.RequestNum) AS req
FROM clickhouse_tutorial.user_tag
SAMPLE 1 / 100
WHERE EventDate = '2014-03-18'
) AS c3,
(
SELECT avg(user_tag.RequestNum) AS req
FROM clickhouse_tutorial.user_tag
SAMPLE 1 / 1000
WHERE EventDate = '2014-03-18'
) AS c4
) AS t
FORMAT Vertical
Query id: 56087e73-25e5-4cee-94ce-9de427bd2b67
Row 1:
──────
c1: 1460.5662128936485
c2: 1460.0685749794536
c3: 1841.2331039827166
c4: 1105.6873169489368
minus(c1, c2): 0.497637914194911
minus(c1, c3): -380.66689108906803
minus(c1, c4): 354.87889594471176
1 rows in set. Elapsed: 0.049 sec. Processed 5.53 million rows, 66.42 MB (113.86 million rows/s., 1.37 GB/s.)
1.1.5. JOIN子句
语法
SELECT <expr_list>
FROM <left_table: (subquery)|table>
[GLOBAL] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI] JOIN<right_table: (subquery)|table>
(ON <expr_list>)|(USING <column_list>)
功能说明
JOIN 连接操作,对应关系代数中的 JOIN算子。通过具有相同值的列,组合连接一个或多个表,生成一个新表。ON <expr_list> 子句中的表达式和 USING <column_list> 子句中的列称为“连接键”。除非另有说明,否则 JOIN操作从具有匹配“连接键”的行,生成笛卡尔积(Cartesian Product),这可能会产生比源表多得多的行数据。
ClickHouse支持SQL标准JOIN类型:
1. INNER JOIN,只返回匹配的行。直接使用 JOIN,等同于INNER JOIN。
2. LEFT OUTER JOIN,除了匹配的行之外,还返回左表中的非匹配行。可以省略关键字OUTER。
3. RIGHT OUTER JOIN,除了匹配行之外,还返回右表中的非匹配行。可以省略关键字OUTER。
4. FULL OUTER JOIN,除了匹配行之外,还返回两个表中的不匹配行。可以省略关键字OUTER。
5. CROSS JOIN,产生整个表的笛卡尔积,没有指定“连接键”。 CROSS JOIN 的替代语法是在 FROM 子句中指定多个表,然后用逗号 ( , ) 分隔。
实例讲解
SELECT uniqExact(t1.UserID)
FROM
(
SELECT UserID
FROM clickhouse_tutorial.user_tag
WHERE EventDate = '2014-03-18'
) AS t1
INNER JOIN
(
SELECT UserID
FROM clickhouse_tutorial.user_tag
WHERE EventDate = '2014-03-19'
) AS t2 ON t1.UserID = t2.UserID
Query id: c5fd19ab-4826-4e66-ab64-359135a18410
┌─uniqExact(UserID)─┐
│ 18496 │
└───────────────────┘
1 rows in set. Elapsed: 1.179 sec. Processed 2.79 million rows, 27.89 MB (2.37 million rows/s., 23.65 MB/s.)
但是,很多场景中通常不用 JOIN(性能比较差),而是用 IN 算子。上面的 SQL 可以用 IN 算子来实现:
SELECT uniqExact(UserID)
FROM clickhouse_tutorial.user_tag
WHERE (EventDate = '2014-03-18') AND (UserID IN (
SELECT UserID
FROM clickhouse_tutorial.user_tag
WHERE EventDate = '2014-03-19'
))
Query id: ade81f51-43e9-4dbb-8e31-361a3831cc58
┌─uniqExact(UserID)─┐
│ 18496 │
└───────────────────┘
1 rows in set. Elapsed: 0.035 sec. Processed 2.79 million rows, 27.89 MB (80.49 million rows/s., 804.87 MB/s.)
可以看到,使用 IN 算子(分布式查询使用 GLOBAL IN),处理速度明显提升,RT性能提升了约1.179/0.035≈34倍。
在执行JOIN时,ClickHouse对执行的顺序没有特别优化,JOIN操作会在WHERE以及聚合查询前运行。
JOIN性能说明
关于 JOIN 查询性能,有以下几点值得注意:
1. JOIN操作结果不会缓存,所以每次JOIN操作都会生成一个全新的执行计划。
2. 如果应用程序会大量使用JOIN,需考虑借助上层应用侧的缓存服务,或使用JOIN表引擎来改善性能。JOIN表引擎会在内存中保存JOIN结果。JOIN表引擎不支持ASOF精度。
3. 在很多情况下,IN的效率比JOIN要高。
4. 在使用JOIN连接维度表时,性能可能并不会很好。因为右则表对每个查询来说,都需要加载一次。在这种情况下,使用外部字典(external dictionaries),会比JOIN性能更好。
默认情况下,ClickHouse使用Hash Join 算法,它会将右表(right table)加载到内存,并为它创建一个hash table。不过,当内存使用到达一个阈值后,ClickHouse会转而使用Merge Join 算法。可以通过以下参数限制JOIN操作消耗的内存:
max_rows_in_join:限制hash table中的行数
max_bytes_in_join:限制hash table的大小
在达到任何上述limit后,ClickHouse会以join_overflow_mode参数值进行响应动作。其中,join_overflow_mode参数包含2个可选值:
THROW:抛出异常并终止操作
BREAK:终止操作但并不抛出异常
1.1.6. PREWHERE子句
语法
PREWHERE expr
功能说明
PREWHERE 子句与 WHERE 子句具有相同的含义。不同之处在于读取数据过程。PREWHERE读取数据过程如下:
1. 读取执行 PREWHERE expr表达式中所需的列,然后进行数据过滤;
2. 再读取 SELECT声明的其他列字段,补全其余属性,进行数据计算。
在某些场合下,PREWHERE 子句比WHERE子句处理的数据量更少、性能更高。
一个查询可以同时指定 PREWHERE 和 WHERE。在这种情况下,PREWHERE 在 WHERE 之前。
PREWHERE 仅支持 *MergeTree 系列表引擎。
ClickHouse提供了自动优化的功能,会在条件合适的情况下将WHERE替换为PREWHERE。
ClickHouse默认开启优化功能,在配置项optimize_move_to_prewhere中设置。取值说明:
0 — 禁用自动 PREWHERE 优化。
1 — 启用自动 PREWHERE 优化。
去系统表system.settings查看optimize_move_to_prewhere配置项详情:
SELECT *
FROM system.settings
WHERE name = 'optimize_move_to_prewhere'
FORMAT Vertical
Query id: e10fc37d-f2a3-4f67-9529-48fc1f68f894
Row 1:
──────
name: optimize_move_to_prewhere
value: 1
changed: 0
description: Allows disabling WHERE to PREWHERE optimization in SELECT queries from MergeTree.
min: ᴺᵁᴸᴸ
max: ᴺᵁᴸᴸ
readonly: 0
type: Bool
实例讲解
SQL 实例
执行如下两个查询 SQL:
SELECT count(UserID)
FROM clickhouse_tutorial.user_tag
PREWHERE user_tag.RequestNum > 100
Query id: 3bde88b6-c994-47d1-97c6-4a259f39efdf
┌─count(UserID)─┐
│ 29244110 │
└───────────────┘
1 rows in set. Elapsed: 0.156 sec. Processed 62.12 million rows, 745.41 MB (397.27 million rows/s., 4.77 GB/s.)
SELECT count(UserID)
FROM clickhouse_tutorial.user_tag
WHERE user_tag.RequestNum > 100
SETTINGS optimize_move_to_prewhere = 0
Query id: 5d9d0927-c111-41aa-a6b7-e3d6884f4a16
┌─count(UserID)─┐
│ 29244110 │
└───────────────┘
1 rows in set. Elapsed: 0.174 sec. Processed 62.12 million rows, 745.41 MB (356.26 million rows/s., 4.28 GB/s.)
性能对比
WHERE 与PREWHERE 性能数据简单对比如下:
WHERE | PREWHERE | |
Elapsed(sec) | 0.174 | 0.156 |
Processed rows/s | 356.26 million | 397.27 million |
Processed GB/s | 4.28 | 4.77 |
可见PREWHERE 的性能要比WHERE 的性能好,但是从上面的测试数据看,性能差距倒也不是十分明显。但是,两者背后的数据计算逻辑是完全不同的。
WHERE与PREWHERE的执行计划
我们可以查看 SQL 执行计划来具体看看两者的不同。
WHERE 的执行计划
EXPLAIN
SELECT count(UserID)
FROM clickhouse_tutorial.user_tag
WHERE user_tag.RequestNum > 100
SETTINGS optimize_move_to_prewhere = 0
Query id: 43a7d961-3fe1-4f2b-b393-c902648a4023
┌─explain─────────────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│ Aggregating │
│ Expression (Before GROUP BY) │
│ Filter (WHERE) │
│ SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│ ReadFromMergeTree │
└─────────────────────────────────────────────────────────────────────────────────┘
6 rows in set. Elapsed: 0.002 sec.
PREWHERE 的执行计划
EXPLAIN
SELECT count(UserID)
FROM clickhouse_tutorial.user_tag
PREWHERE user_tag.RequestNum > 100
Query id: cf987788-3e16-4dc9-8991-9bde689d13c0
┌─explain───────────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│ Aggregating │
│ Expression (Before GROUP BY) │
│ SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│ ReadFromMergeTree │
└───────────────────────────────────────────────────────────────────────────────┘
5 rows in set. Elapsed: 0.002 sec.
可以看出,“Filter (WHERE)”阶段是 WHERE 比 PREWHERE 多出来的。
1.1.7. WHERE子句
WHERE子句表达式指定了要检索的行。对应到关系代数中的选择操作。
语法
WHERE expr
功能说明
WHERE可以通过表达式来过滤数据。如果过滤条件恰好为主键字段,则可以进一步借助索引加速查询。所以,WHERE子句是决定查询语句是否能使用索引的判断依据(前提是表引擎支持索引)。
实例讲解
SELECT
avg(RequestNum) AS req,
Age AS age
FROM clickhouse_tutorial.user_tag
WHERE (age != 0) AND (EventDate = toDate('2014-03-18'))
GROUP BY Age
ORDER BY req DESC
Query id: 5b303d93-8abb-495f-bad8-494429bfa06e
┌────────────────req─┬─age─┐
│ 1953.3613856847173 │ 16 │
│ 1893.3540361841049 │ 55 │
│ 1705.0050234410571 │ 39 │
│ 924.8175841151426 │ 22 │
│ 802.432123869042 │ 26 │
└────────────────────┴─────┘
5 rows in set. Elapsed: 0.058 sec. Processed 13.84 million rows, 96.86 MB (238.43 million rows/s., 1.67 GB/s.)
使用EXPLAIN指令查看 SQL执行计划:
EXPLAIN
SELECT
avg(RequestNum) AS req,
Age AS age
FROM clickhouse_tutorial.user_tag
WHERE (age != 0) AND (EventDate = toDate('2014-03-18'))
GROUP BY Age
ORDER BY req DESC
Query id: 7071326b-35c0-422e-9588-52f1d8957679
Connecting to database system at 127.0.0.1:9009 as user default.
Connected to ClickHouse server version 22.4.1 revision 54455.
┌─explain─────────────────────────────────────────────────────────────────────────────┐
│ Expression (Projection) │
│ Sorting (Sorting for ORDER BY) │
│ Expression (Before ORDER BY) │
│ Aggregating │
│ Expression (Before GROUP BY) │
│ Filter (WHERE) │
│ SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│ ReadFromMergeTree │
└─────────────────────────────────────────────────────────────────────────────────────┘
8 rows in set. Elapsed: 0.008 sec.
1.1.8. GROUP BY子句
语法
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
功能说明
GROUP BY 子句将 SELECT 查询切换为聚合计算模式。GROUP BY expr_list子句,包含一个表达式或者多个表达式的列表(多个表达式之间用逗号分隔),该列表充当“分组键”,而每个单独的表达式将被称为“键表达式”。
实例讲解
SELECT
count(),
sum(RequestNum) AS s,
avg(RequestNum) AS a,
max(RequestNum) AS m,
EventDate,
OS
FROM clickhouse_tutorial.user_tag
WHERE RequestNum != 0
GROUP BY
EventDate,
OS
ORDER BY a DESC
LIMIT 7
Query id: 09915fed-acfe-49cb-9b37-6e5cfcc17715
┌─count()─┬─────────s─┬──────────────────a─┬──────m─┬──EventDate─┬──OS─┐
│ 20 │ 3602510 │ 180125.5 │ 195583 │ 2014-03-21 │ 25 │
│ 240 │ 24593020 │ 102470.91666666667 │ 522032 │ 2014-03-22 │ 63 │
│ 90 │ 2377190 │ 26413.222222222223 │ 32513 │ 2014-03-18 │ 6 │
│ 10 │ 71020 │ 7102 │ 7102 │ 2014-03-17 │ 14 │
│ 12310 │ 78834610 │ 6404.111291632818 │ 16381 │ 2014-03-17 │ 97 │
│ 1070 │ 6374100 │ 5957.102803738318 │ 16368 │ 2014-03-18 │ 4 │
│ 27640 │ 153955650 │ 5570.030752532562 │ 32749 │ 2014-03-21 │ 105 │
└─────────┴───────────┴────────────────────┴────────┴────────────┴─────┘
7 rows in set. Elapsed: 0.288 sec. Processed 88.74 million rows, 621.17 MB (308.15 million rows/s., 2.16 GB/s.)
查看 SQL 执行计划:
EXPLAIN actions = 1
SELECT
count(),
sum(RequestNum) AS s,
avg(RequestNum) AS a,
max(RequestNum) AS m,
EventDate,
OS
FROM clickhouse_tutorial.user_tag
WHERE RequestNum != 0
GROUP BY
EventDate,
OS
ORDER BY a DESC
LIMIT 7
Query id: c5aee861-cb73-4089-bbdd-412c099f7037
┌─explain─────────────────────────────────────────────────────────────────────────────┐
│ Expression (Projection) │
│ Actions: INPUT :: 0 -> EventDate Date : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> count() UInt64 : 2 │
│ INPUT : 3 -> sum(RequestNum) UInt64 : 3 │
│ INPUT : 4 -> avg(RequestNum) Float64 : 4 │
│ INPUT : 5 -> max(RequestNum) UInt32 : 5 │
│ ALIAS sum(RequestNum) :: 3 -> s UInt64 : 6 │
│ ALIAS avg(RequestNum) :: 4 -> a Float64 : 3 │
│ ALIAS max(RequestNum) :: 5 -> m UInt32 : 4 │
│ Positions: 2 6 3 4 0 1 │
│ Limit (preliminary LIMIT (without OFFSET)) │
│ Limit 7 │
│ Offset 0 │
│ Sorting (Sorting for ORDER BY) │
│ Sort description: avg(RequestNum) DESC │
│ Limit 7 │
│ Expression (Before ORDER BY) │
│ Actions: INPUT :: 0 -> EventDate Date : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> count() UInt64 : 2 │
│ INPUT :: 3 -> sum(RequestNum) UInt64 : 3 │
│ INPUT :: 4 -> avg(RequestNum) Float64 : 4 │
│ INPUT :: 5 -> max(RequestNum) UInt32 : 5 │
│ Positions: 0 1 2 3 4 5 │
│ Aggregating │
│ Keys: EventDate, OS │
│ Aggregates: │
│ count() │
│ Function: count() → UInt64 │
│ Arguments: none │
│ Argument positions: none │
│ sum(RequestNum) │
│ Function: sum(UInt32) → UInt64 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ avg(RequestNum) │
│ Function: avg(UInt32) → Float64 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ max(RequestNum) │
│ Function: max(UInt32) → UInt32 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ Expression (Before GROUP BY) │
│ Actions: INPUT :: 0 -> RequestNum UInt32 : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> EventDate Date : 2 │
│ Positions: 0 1 2 │
│ SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│ ReadFromMergeTree │
│ ReadType: Default │
│ Parts: 41 │
│ Granules: 10843 │
└─────────────────────────────────────────────────────────────────────────────────────┘
54 rows in set. Elapsed: 0.004 sec.
如果SELECT后面只有聚合函数,没有选择其他字段,则GROUP BY 关键字可以省略。例如:
SELECT
SUM(data_compressed_bytes) AS compressed,
SUM(data_uncompressed_bytes) AS uncompressed
FROM system.parts
Query id: 7af31340-a047-416c-961e-615f29ee7881
┌─compressed─┬─uncompressed─┐
│ 2601173868 │ 9355198237 │
└────────────┴──────────────┘
1 rows in set. Elapsed: 0.006 sec.
1.1.9. HAVING子句
语法
...
WHERE expr
GROUP BY expr_list
HAVING expr
ORDER BY expr_list
功能说明
1. 在GROUP BY expr_list子句之后使用HAVING expr,用来过滤满足表达式的数据行。
2. 可以通过别名引用 HAVING 子句中的 SELECT 子句的聚合结果。
3. HAVING expr子句类似于 WHERE expr子句,但不同的是 WHERE 在聚合之前执行,而 HAVING 在聚合之后执行。
4. 如果不执行聚合计算,则不能使用 HAVING,改用 WHERE。
实例讲解
SELECT
count(),
sum(RequestNum) AS s,
avg(RequestNum) AS a,
max(RequestNum) AS m,
EventDate,
OS
FROM clickhouse_tutorial.user_tag
WHERE RequestNum != 0
GROUP BY
EventDate,
OS
HAVING a > 100000 --直接使用别名 a
ORDER BY a DESC
LIMIT 7
Query id: e8320bd7-c7cd-4e83-ab7d-73d1dc94e140
┌─count()─┬────────s─┬──────────────────a─┬──────m─┬──EventDate─┬─OS─┐
│ 20 │ 3602510 │ 180125.5 │ 195583 │ 2014-03-21 │ 25 │
│ 240 │ 24593020 │ 102470.91666666667 │ 522032 │ 2014-03-22 │ 63 │
└─────────┴──────────┴────────────────────┴────────┴────────────┴────┘
2 rows in set. Elapsed: 0.312 sec. Processed 88.74 million rows, 621.17 MB (284.44 million rows/s., 1.99 GB/s.)
查看 SQL 执行计划:
EXPLAIN actions = 1
SELECT
count(),
sum(RequestNum) AS s,
avg(RequestNum) AS a,
max(RequestNum) AS m,
EventDate,
OS
FROM clickhouse_tutorial.user_tag
WHERE RequestNum != 0
GROUP BY
EventDate,
OS
HAVING a > 100000
ORDER BY a DESC
LIMIT 7
Query id: a8b4624f-12b5-48ec-a44c-1408edafff13
┌─explain───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Expression (Projection) │
│ Actions: INPUT :: 0 -> EventDate Date : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> count() UInt64 : 2 │
│ INPUT : 3 -> sum(RequestNum) UInt64 : 3 │
│ INPUT : 4 -> avg(RequestNum) Float64 : 4 │
│ INPUT : 5 -> max(RequestNum) UInt32 : 5 │
│ ALIAS sum(RequestNum) :: 3 -> s UInt64 : 6 │
│ ALIAS avg(RequestNum) :: 4 -> a Float64 : 3 │
│ ALIAS max(RequestNum) :: 5 -> m UInt32 : 4 │
│ Positions: 2 6 3 4 0 1 │
│ Limit (preliminary LIMIT (without OFFSET)) │
│ Limit 7 │
│ Offset 0 │
│ Sorting (Sorting for ORDER BY) │
│ Sort description: avg(RequestNum) DESC │
│ Limit 7 │
│ Expression (Before ORDER BY) │
│ Actions: INPUT :: 0 -> EventDate Date : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> count() UInt64 : 2 │
│ INPUT :: 3 -> sum(RequestNum) UInt64 : 3 │
│ INPUT :: 4 -> avg(RequestNum) Float64 : 4 │
│ INPUT :: 5 -> max(RequestNum) UInt32 : 5 │
│ Positions: 0 1 2 3 4 5 │
│ Filter (HAVING) │
│ Filter column: greater(avg(RequestNum), 100000) (removed) │
│ Actions: INPUT :: 0 -> EventDate Date : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> count() UInt64 : 2 │
│ INPUT :: 3 -> sum(RequestNum) UInt64 : 3 │
│ INPUT : 4 -> avg(RequestNum) Float64 : 4 │
│ INPUT :: 5 -> max(RequestNum) UInt32 : 5 │
│ COLUMN Const(UInt32) -> 100000 UInt32 : 6 │
│ FUNCTION greater(avg(RequestNum) : 4, 100000 :: 6) -> greater(avg(RequestNum), 100000) UInt8 : 7 │
│ Positions: 0 1 2 3 4 5 7 │
│ Aggregating │
│ Keys: EventDate, OS │
│ Aggregates: │
│ count() │
│ Function: count() → UInt64 │
│ Arguments: none │
│ Argument positions: none │
│ sum(RequestNum) │
│ Function: sum(UInt32) → UInt64 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ avg(RequestNum) │
│ Function: avg(UInt32) → Float64 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ max(RequestNum) │
│ Function: max(UInt32) → UInt32 │
│ Arguments: RequestNum │
│ Argument positions: 0 │
│ Expression (Before GROUP BY) │
│ Actions: INPUT :: 0 -> RequestNum UInt32 : 0 │
│ INPUT :: 1 -> OS UInt8 : 1 │
│ INPUT :: 2 -> EventDate Date : 2 │
│ Positions: 0 1 2 │
│ SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│ ReadFromMergeTree │
│ ReadType: Default │
│ Parts: 41 │
│ Granules: 10843 │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
65 rows in set. Elapsed: 0.005 sec.
1.1.10. SELECT子句
SELECT查询子句指定了返回的列或 SQL表达式的列表。对应关系代数中的投影操作。
语法
SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
功能说明
SELECT 子句中指定的表达式列表expr_list,是在上面的所有子句操作完成后开始计算的。表达式列表expr_list,是由逗号分隔的一个或多个表达式组成。表达式可以是函数、标识符、文字、运算符的应用程序、括号中的表达式、子查询或星号,还可以包含别名;表达式可以作为函数和运算符的入参。
实例讲解
查询字段EventDate值,聚合计算uniqExact(UserID)的值、sum(RequestNum)的值 ,并给结果分别指定返回别名,SQL 实例如下:
WITH (
SELECT max(EventTime)
FROM clickhouse_tutorial.user_tag
) AS ts_upper_bound
SELECT
uniqExact(UserID) AS userCnt,
sum(Reque以上是关于ClickHouse高级数据查询SQL: WITH/JOIN/IN/INTO OUTFILE/嵌套子查询/交并差计算等的主要内容,如果未能解决你的问题,请参考以下文章
《ClickHouse企业级应用:入门进阶与实战》6 ClickHouse SQL基础
大数据ClickHouse进阶:ClickHouse的with子句
ClickHouse-尚硅谷(10. 高级-语法优化规则)学习笔记
ClickHouseHangout with ClickHouse