clickhouse的SQL参考——(一)select

参考资料

https://clickhouse.tech/docs/en/sql-reference/statements/select/

版本:v20.11

目录

参考资料

总览

语法

select字句

COLUMNS表达式

星号

极值

AS

实施细节

SELECT修饰符 

APPLY

EXCEPT

REPLACE

Modifier Combinations

ARRAY JOIN 子句

语法

例子

使用别名

具有嵌套数据结构的ARRAY JOIN

实施细节

DISTINCT子句

空值处理

和groupby的不同

局限性

例子

FORMAT子句

默认格式

实施细节

例子

FROM子句

FINAL修饰符

缺点

实施细节

GROUP BY子句

NULL值处理

WITH ROLLUP修饰符

举例

WITH CUBE修饰符

举例

WITH TOTALS修饰符

totals处理配置

实施细节

外部存储器中的GROUP BY

HAVING子句

限制

INTO OUTFILE子句

实施细节

JOIN子句

语法

支持的join类型

设置

ASOF JOIN 使用

举例

分布式联接

使用建议

空值处理

句法

语法限制

性能

内存限制

举例

LIMIT子句

LIMIT…WITH TIES修饰符

LIMIT BY子句

举例

ORDER BY子句

特殊值排序

举例

大小写支持

实施细节

优化数据读取

ORDER BY Expr WITH FILL修饰符

举例

OFFSET FETCH修饰符

举例

PREWHERE子句

手动控制前置位置

限制

SAMPLE子句

SAMPLE K

SAMPLE N

SAMPLE K OFFSET M

UNION子句

UNION ALL

UNION DISTINCT

实施细节

WHERE子句

WITH子句

语法

举例


总览

语法

[WITH expr_list|(subquery)]
SELECT [DISTINCT] 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 )|(USING )
[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]
[UNION  ...]
[INTO OUTFILE filename]
[FORMAT format]

除了紧随在SELECT之后的必需的表达式列表(将在下面更详细介绍)之外,其他所有子句都是可选的。

  • WITH clause
  • FROM clause
  • SAMPLE clause
  • JOIN clause
  • PREWHERE clause
  • WHERE clause
  • GROUP BY clause
  • LIMIT BY clause
  • HAVING clause
  • SELECT clause
  • DISTINCT clause
  • LIMIT clause
  • UNION clause
  • INTO OUTFILE clause
  • FORMAT clause

select字句

COLUMNS表达式

要将结果中的某些列与re2正则表达式匹配,可以使用该COLUMNS表达式。

COLUMNS('regexp')

--例如,考虑表:
CREATE TABLE default.col_names (aa Int8, ab Int8, bc Int8) ENGINE = TinyLog

--以下查询从a名称中包含符号的所有列中选择数据。
SELECT COLUMNS('a') FROM col_names
┌─aa─┬─ab─┐
│  1 │  1 │
└────┴────┘

--所选列不按字母顺序返回。
--您可以COLUMNS在查询中使用多个表达式并将函数应用于它们。
--例如:
SELECT COLUMNS('a'), COLUMNS('c'), toTypeName(COLUMNS('c')) FROM col_names
┌─aa─┬─ab─┬─bc─┬─toTypeName(bc)─┐
│  1 │  1 │  1 │ Int8           │
└────┴────┴────┴────────────────┘

--COLUMNS表达式返回的每一列都作为单独的参数传递给函数。如果支持其他参数,您也可以将其传递给该函数。使用功能时要小心。如果函数不支持传递给它的参数数量,则ClickHouse会引发异常。
--例如:
SELECT COLUMNS('a') + COLUMNS('c') FROM col_names

Received exception from server (version 19.14.1):
Code: 42. DB::Exception: Received from localhost:9000. DB::Exception: Number of arguments for function plus doesn't match: passed 3, should be 2.
--因为COLUMNS('a')返回两列,COLUMNS('c')返回一列,+号没有办法同时操作三个参数

星号

您可以在查询的任何部分放置星号,而不是表达式。分析查询后,星号会展开到所有表列的列表(不包括虚拟列)。

仅在少数情况下使用星号是合理的:

  • 创建表转储时。
  • 对于仅包含几列的表,例如系统表。
  • 用于获取有关表中哪些列的信息。在这种情况下,请设置LIMIT 1。但是最好使用DESC TABLE查询。
  • 当对少量色谱柱进行强过滤时,使用PREWHERE
  • 在子查询中(因为外部查询不需要的列从子查询中排除)。

在所有其他情况下,我们不建议使用星号,因为它只会给您带来柱状DBMS的缺点,而不是优点。换句话说,不建议使用星号。

极值

除了结果之外,您还可以获取结果列的最小值和最大值。

为此,将 extremes 设置为1。将为数字类型,日期和带时间的日期计算最小值和最大值。对于其他列,将输出默认值。

计算出另外两行–分别为最小值和最大值。 这些额外的两行以JSON *,TabSeparated *和Pretty *格式输出,与其他行分开。 它们不会以其他格式输出。

  • 在JSON *格式中,极限值在单独的“extremes”字段中输出。
  • 在TabSeparated *格式中,该行位于主要结果之后,并且在“totals”之后(如果存在)。 它之前是一个空行(在其他数据之后)。
  • 在Pretty *格式中,该行在主要结果之后和总计(如果存在)之后作为单独的表格输出。

将为LIMIT之前但LIMIT BY之后的行计算极值。
但是,当使用LIMIT偏移量,大小时,偏移量之前的行将包含在极端中。
在流请求中,结果还可能包含少量通过LIMIT的行。

AS

您可以在查询的任何部分使用同义词(AS别名)。
GROUP BY和ORDER BY子句不支持位置参数。 这与MySQL矛盾,但符合标准SQL。 例如,GROUP BY 1、2将被解释为按常量分组(即,将所有行聚合为一个)。

实施细节

如果查询省略了DISTINCT,GROUP BY和ORDER BY子句以及IN和JOIN子查询,则将使用O(1)数量的RAM对查询进行完全的流处理。

如果没有省略,则需要指定适当的限制,否则查询可能会消耗大量RAM。设置项:

  • max_memory_usage
  • max_rows_to_group_by
  • max_rows_to_sort
  • max_rows_in_distinct
  • max_bytes_in_distinct
  • max_rows_in_set
  • max_bytes_in_set
  • max_rows_in_join
  • max_bytes_in_join
  • max_bytes_before_external_sort
  • max_bytes_before_external_group_by

更多信息查看常规设置项。

SELECT修饰符 

您可以在SELECT查询中使用以下修饰符。

APPLY

允许您为通过查询的外部表表达式返回的每一行调用一些函数。

SELECT APPLY( ) FROM [db.]table_name
 

INSERT INTO columns_transformers VALUES (100, 10, 324), (120, 8, 23);
SELECT * APPLY(sum) FROM columns_transformers;
┌─sum(i)─┬─sum(j)─┬─sum(k)─┐
│    220 │     18 │    347 │
└────────┴────────┴────────┘

EXCEPT

从结果中排除一列或多列的名称。 输出中将省略所有匹配的列名。

SELECT EXCEPT ( col_name1 [, col_name2, col_name3, ...] ) FROM [db.]table_name

SELECT * EXCEPT (i) from columns_transformers;
┌──j─┬───k─┐
│ 10 │ 324 │
│  8 │  23 │
└────┴─────┘

REPLACE

指定一个或多个表达式别名。每个别名必须与SELECT *语句中的列名匹配。

在输出列列表中,与别名匹配的列被该REPLACE中的表达式替换。此修饰符不会更改列的名称或顺序。 但是,它可以更改值和值类型。

SELECT REPLACE( AS col_name) from [db.]table_name

SELECT * REPLACE(i + 1 AS i) from columns_transformers;
┌───i─┬──j─┬───k─┐
│ 101 │ 10 │ 324 │
│ 121 │  8 │  23 │
└─────┴────┴─────┘

Modifier Combinations

您可以单独使用每个修饰符,也可以组合使用它们。

--多次使用相同的修饰符。
SELECT COLUMNS('[jk]') APPLY(toString) APPLY(length) APPLY(max) from columns_transformers;
┌─max(length(toString(j)))─┬─max(length(toString(k)))─┐
│                        2 │                        3 │
└──────────────────────────┴──────────────────────────┘

--在单个查询中使用多个修饰符。
SELECT * REPLACE(i + 1 AS i) EXCEPT (j) APPLY(sum) from columns_transformers;
┌─sum(plus(i, 1))─┬─sum(k)─┐
│             222 │    347 │
└─────────────────┴────────┘

 

ARRAY JOIN 子句

对于包含数组列的表来说,这是一种常见的操作,以产生一个新表,该表具有一个包含该初始列的每个单独数组元素的列,而其他列的值则重复。 这是ARRAY JOIN子句的基本情况。

它可以看作是使用数组或嵌套数据结构执行JOIN。 目的类似于arrayJoin函数,但子句功能更广泛。

语法

SELECT 
FROM 
[LEFT] ARRAY JOIN 
[WHERE|PREWHERE ]
...

您只能在SELECT查询中指定一个ARRAY JOIN子句。

下面列出了受支持的ARRAY JOIN类型:

  • ARRAY JOIN-在基本情况下,JOIN的结果中不包含空数组。
  • LEFT JOIN-JOIN-结果包含具有空数组的行。 空数组的值设置为数组元素类型的默认值(通常为0,空字符串或NULL)。

例子

下面的示例演示了ARRAY JOIN和LEFT ARRAY JOIN子句的用法。

CREATE TABLE arrays_test
(
    s String,
    arr Array(UInt8)
) ENGINE = Memory;

INSERT INTO arrays_test
VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []);
┌─s───────────┬─arr─────┐
│ Hello       │ [1,2]   │
│ World       │ [3,4,5] │
│ Goodbye     │ []      │
└─────────────┴─────────┘

--使用ARRAY JOIN的例子:
SELECT s, arr
FROM arrays_test
ARRAY JOIN arr;
┌─s─────┬─arr─┐
│ Hello │   1 │
│ Hello │   2 │
│ World │   3 │
│ World │   4 │
│ World │   5 │
└───────┴─────┘

--使用LEFT ARRAY JOIN的例子:
SELECT s, arr
FROM arrays_test
LEFT ARRAY JOIN arr;
┌─s───────────┬─arr─┐
│ Hello       │   1 │
│ Hello       │   2 │
│ World       │   3 │
│ World       │   4 │
│ World       │   5 │
│ Goodbye     │   0 │
└─────────────┴─────┘

使用别名

可以在ARRAY JOIN子句中为数组指定别名。 在这种情况下,可以使用此别名访问数组项,也可以使用原始名称访问数组本身。 例:

SELECT s, arr, a
FROM arrays_test
ARRAY JOIN arr AS a;
┌─s─────┬─arr─────┬─a─┐
│ Hello │ [1,2]   │ 1 │
│ Hello │ [1,2]   │ 2 │
│ World │ [3,4,5] │ 3 │
│ World │ [3,4,5] │ 4 │
│ World │ [3,4,5] │ 5 │
└───────┴─────────┴───┘

--使用别名,您可以对外部数组执行ARRAY JOIN。
SELECT s, arr_external
FROM arrays_test
ARRAY JOIN [1, 2, 3] AS arr_external;
┌─s───────────┬─arr_external─┐
│ Hello       │            1 │
│ Hello       │            2 │
│ Hello       │            3 │
│ World       │            1 │
│ World       │            2 │
│ World       │            3 │
│ Goodbye     │            1 │
│ Goodbye     │            2 │
│ Goodbye     │            3 │
└─────────────┴──────────────┘

--多个数组可以在ARRAY JOIN子句中以逗号分隔。 在这种情况下,JOIN与它们同时执行(直接和,而不是笛卡尔积)。 请注意,所有数组的大小必须相同。 例:
SELECT s, arr, a, num, mapped
FROM arrays_test
ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(x -> x + 1, arr) AS mapped;
┌─s─────┬─arr─────┬─a─┬─num─┬─mapped─┐
│ Hello │ [1,2]   │ 1 │   1 │      2 │
│ Hello │ [1,2]   │ 2 │   2 │      3 │
│ World │ [3,4,5] │ 3 │   1 │      4 │
│ World │ [3,4,5] │ 4 │   2 │      5 │
│ World │ [3,4,5] │ 5 │   3 │      6 │
└───────┴─────────┴───┴─────┴────────┘


--下面的示例使用arrayEnumerate函数:
SELECT s, arr, a, num, arrayEnumerate(arr)
FROM arrays_test
ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num;
┌─s─────┬─arr─────┬─a─┬─num─┬─arrayEnumerate(arr)─┐
│ Hello │ [1,2]   │ 1 │   1 │ [1,2]               │
│ Hello │ [1,2]   │ 2 │   2 │ [1,2]               │
│ World │ [3,4,5] │ 3 │   1 │ [1,2,3]             │
│ World │ [3,4,5] │ 4 │   2 │ [1,2,3]             │
│ World │ [3,4,5] │ 5 │   3 │ [1,2,3]             │
└───────┴─────────┴───┴─────┴─────────────────────┘

具有嵌套数据结构的ARRAY JOIN

CREATE TABLE nested_test
(
    s String,
    nest Nested(
    x UInt8,
    y UInt32)
) ENGINE = Memory;

INSERT INTO nested_test
VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], []);
┌─s───────┬─nest.x──┬─nest.y─────┐
│ Hello   │ [1,2]   │ [10,20]    │
│ World   │ [3,4,5] │ [30,40,50] │
│ Goodbye │ []      │ []         │
└─────────┴─────────┴────────────┘

SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN nest;
┌─s─────┬─nest.x─┬─nest.y─┐
│ Hello │      1 │     10 │
│ Hello │      2 │     20 │
│ World │      3 │     30 │
│ World │      4 │     40 │
│ World │      5 │     50 │
└───────┴────────┴────────┘

--在ARRAY JOIN中指定嵌套数据结构的名称时,其含义与ARRAY JOIN及其组成的所有数组元素相同。 示例如下:
SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN `nest.x`, `nest.y`;
┌─s─────┬─nest.x─┬─nest.y─┐
│ Hello │      1 │     10 │
│ Hello │      2 │     20 │
│ World │      3 │     30 │
│ World │      4 │     40 │
│ World │      5 │     50 │
└───────┴────────┴────────┘


--另一种变化:

SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN `nest.x`;
┌─s─────┬─nest.x─┬─nest.y─────┐
│ Hello │      1 │ [10,20]    │
│ Hello │      2 │ [10,20]    │
│ World │      3 │ [30,40,50] │
│ World │      4 │ [30,40,50] │
│ World │      5 │ [30,40,50] │
└───────┴────────┴────────────┘

--别名可用于嵌套数据结构,以便选择JOIN结果或源数组。 例:
SELECT s, `n.x`, `n.y`, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN nest AS n;
┌─s─────┬─n.x─┬─n.y─┬─nest.x──┬─nest.y─────┐
│ Hello │   1 │  10 │ [1,2]   │ [10,20]    │
│ Hello │   2 │  20 │ [1,2]   │ [10,20]    │
│ World │   3 │  30 │ [3,4,5] │ [30,40,50] │
│ World │   4 │  40 │ [3,4,5] │ [30,40,50] │
│ World │   5 │  50 │ [3,4,5] │ [30,40,50] │
└───────┴─────┴─────┴─────────┴────────────┘


--使用arrayEnumerate函数的示例:
SELECT s, `n.x`, `n.y`, `nest.x`, `nest.y`, num
FROM nested_test
ARRAY JOIN nest AS n, arrayEnumerate(`nest.x`) AS num;
┌─s─────┬─n.x─┬─n.y─┬─nest.x──┬─nest.y─────┬─num─┐
│ Hello │   1 │  10 │ [1,2]   │ [10,20]    │   1 │
│ Hello │   2 │  20 │ [1,2]   │ [10,20]    │   2 │
│ World │   3 │  30 │ [3,4,5] │ [30,40,50] │   1 │
│ World │   4 │  40 │ [3,4,5] │ [30,40,50] │   2 │
│ World │   5 │  50 │ [3,4,5] │ [30,40,50] │   3 │
└───────┴─────┴─────┴─────────┴────────────┴─────┘

实施细节

运行ARRAY JOIN时,查询执行顺序已优化。

尽管必须在查询的WHERE / PREWHERE子句之前指定ARRAY JOIN,但从技术上讲,它们可以按任意顺序执行,除非使用ARRAY JOIN的结果进行过滤。

处理顺序由查询优化器控制。

DISTINCT子句

如果指定了SELECT DISTINCT,则查询结果中仅保留唯一行。 因此,结果中所有完全匹配的行集中仅剩下一行。

空值处理

DISTINCT与NULL一起使用,认为NULL是特定值,并且NULL == NULL。

换句话说,在DISTINCT结果中,与NULL的不同组合仅发生一次。 在大多数其他情况下,它与NULL处理不同。

和groupby的不同

使用distinct的结果和使用groupby但是不使用聚合函数的结果很有可能相同。不同点:

  • DISTINCT可以与GROUP BY一起应用。
  • 当省略ORDER BY并定义LIMIT时,查询将在读取所需数量的不同行后立即停止运行。
  • 数据块在处理时输出,而无需等待整个查询完成运行。

局限性

当 SELECT 包含有数组列,不支持distinct。

例子

--ClickHouse支持使用 DISTINCT 和 ORDER BY 在一个查询中的不同的列。 DISTINCT 子句在 ORDER BY 子句前被执行。
--示例表:
┌─a─┬─b─┐
│ 2 │ 1 │
│ 1 │ 2 │
│ 3 │ 3 │
│ 2 │ 4 │
└───┴───┘

--当执行 SELECT DISTINCT a FROM t1 ORDER BY b ASC 来查询数据,我们得到以下结果:
┌─a─┐
│ 2 │
│ 1 │
│ 3 │
└───┘

--如果我们改变排序方向 SELECT DISTINCT a FROM t1 ORDER BY b DESC,我们得到以下结果:
┌─a─┐
│ 3 │
│ 1 │
│ 2 │
└───┘
行 2, 4 排序前被切割。

FORMAT子句

ClickHouse支持广泛的序列化格式,这些格式可用于查询结果等。 有多种选择SELECT输出格式的方法,其中一种方法是在查询结束时指定FORMAT格式,以获取任何特定格式的结果数据。

为了方便起见,与其他系统集成或提高性能,可以使用特定格式。

默认格式

如果省略FORMAT子句,则使用默认格式,这取决于设置和用于访问ClickHouse服务器的接口。

  • 对于批处理模式下的HTTP接口和命令行客户端,默认格式为TabSeparated。
  • 对于处于交互方式的命令行客户端,默认格式为PrettyCompact。

实施细节

使用命令行客户端时,数据始终以内部有效格式(本机)通过网络传递。 客户端独立解释查询的FORMAT子句并格式化数据本身(从而减轻了网络和服务器的额外负担)。

例子

--使用TabSeparated格式
vm:) select * from rmTest format TabSeparated;

SELECT *
FROM rmTest
FORMAT TabSeparated

1	lisi	2020-10-10	90
1	zhangsan	2020-10-10	88
1	zhangsan	2020-10-11	89
15	lmt	2020-11-24	1
16	lmt	2020-11-24	1

5 rows in set. Elapsed: 0.011 sec. 

--使用默认格式
vm:) select * from rmTest;

SELECT *
FROM rmTest

┌─id─┬─name─┬───────date─┬─score─┐
│ 15 │ lmt  │ 2020-11-24 │     1 │
│ 16 │ lmt  │ 2020-11-24 │     1 │
└────┴──────┴────────────┴───────┘
┌─id─┬─name─────┬───────date─┬─score─┐
│  1 │ zhangsan │ 2020-10-11 │    89 │
└────┴──────────┴────────────┴───────┘
┌─id─┬─name─────┬───────date─┬─score─┐
│  1 │ lisi     │ 2020-10-10 │    90 │
│  1 │ zhangsan │ 2020-10-10 │    88 │
└────┴──────────┴────────────┴───────┘

5 rows in set. Elapsed: 0.006 sec.

FROM子句

FROM子句指定读取数据来源:

  • Table
  • Subquery
  • Table function

JOIN和ARRAY JOIN子句也可用于扩展FROM子句的功能。

子查询是另一个SELECT查询,可以在FROM子句中的括号中指定。

FROM子句可以包含多个数据源,以逗号分隔,等效于对它们执行CROSS JOIN。

FINAL修饰符

指定FINAL后,ClickHouse会在返回结果之前完全合并数据,执行在合并期间发生的数据转换。

FINAL修饰符支持:

  • 使用MergeTree引擎家族的表(GraphiteMergeTree除外)
  • MergeTree引擎的Replicated版本
  • 通过MergeTree引擎表创建的,可以运行在其他引擎上的View,Buffer,Distributed和MaterializedView

缺点

使用FINAL的查询比不使用FINAL的类似查询执行速度稍慢,原因是:

  • 数据在查询执行期间合并。
  • 除了查询中指定的列外,具有FINAL的查询还读取主键列。

在大多数情况下,请避免使用FINAL。

常见的做法是不同的查询,比如当前mergetree引擎的后台进程还没有发生,可以使用聚合(例如,丢弃重复项)来解决。

实施细节

如果忽略FROM子句,将从system.one表中读取数据。system.one表仅包含一行(该表与其他DBMS中的DUAL表的目的相同)

要执行查询,查询中列出的所有列均从相应的表中提取;查询中不需要的列都从子查询中抛出。

如果查询未列出任何列(例如SELECT count()FROM t),会从表中提取一些列(选出最小的列),以计算行数。

GROUP BY子句

GROUP BY子句将SELECT查询切换到聚合模式,其工作方式如下:

  • GROUP BY子句包含一个表达式列表(或单个表达式,它被视为长度为1的列表)。该列表用作“分组关键字”,而每个单独的表达式将称为“关键字表达式”。
  • SELECT,HAVING和ORDER BY子句中的所有表达式,计算是基于“关键字表达式”或者非“关键字表达式”(普通列)上的集合函数的。换句话说,从表中选择的每一列都必须在键表达式或聚合函数中使用,但不能同时使用。
  • 使用groupby后,查询返回的结果数量和分组键的数量相同,通常会使返回结果减少好几个数量级,但是如果分组键都不同,则结果数据量不会减少。

如果查询中包含的键列都在聚合函数中被使用,那么可以省略掉groupby

NULL值处理

对于分组,ClickHouse将NULL解释为值,并且NULL == NULL。 

假如有表:
┌─x─┬────y─┐
│ 1 │    2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │    2 │
│ 3 │    3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘

SELECT sum(x), y FROM t_null_big GROUP BY y:
┌─sum(x)─┬────y─┐
│      4 │    2 │
│      3 │    3 │
│      5 │ ᴺᵁᴸᴸ │
└────────┴──────┘

如果将多个键传递给GROUP BY,则结果将为您提供选择的所有组合。

WITH ROLLUP修饰符

WITH ROLLUP修饰符用于根据他们在GROUP BY列表中的顺序计算关键值表达式的小计。

小计的计算顺序和给定顺序相反:首先计算列表中最后一个键表达式的小计,然后计算前一个键的小计,依此类推,直到第一个键表达式。

在小计行中,已经“分组”的键表达式的值设置为0或空行。

HAVING子句会影响小计结果。

举例

--考虑表:
┌─year─┬─month─┬─day─┐
│ 2019 │     1 │   5 │
│ 2019 │     1 │  15 │
│ 2020 │     1 │   5 │
│ 2020 │     1 │  15 │
│ 2020 │    10 │   5 │
│ 2020 │    10 │  15 │
└──────┴───────┴─────┘

SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH ROLLUP;

--由于GROUP BY部分具有三个键表达式,因此结果包含四个表,其小计从右到左“汇总”:
--GROUP BY:
--year, month, day
--year, month(day列用零填充)
--year(month, day列用零填充)
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │    10 │  15 │       1 │
│ 2020 │     1 │   5 │       1 │
│ 2019 │     1 │   5 │       1 │
│ 2020 │     1 │  15 │       1 │
│ 2019 │     1 │  15 │       1 │
│ 2020 │    10 │   5 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     1 │   0 │       2 │
│ 2020 │     1 │   0 │       2 │
│ 2020 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     0 │   0 │       2 │
│ 2020 │     0 │   0 │       4 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   0 │       6 │
└──────┴───────┴─────┴─────────┘

WITH CUBE修饰符

WITH CUBE修饰符用于计算GROUP BY列表中每个键表达式组合的小计。 小计行将添加到结果表之后。

在小计行中,所有“分组”键表达式的值都设置为0或空行。

HAVING子句会影响小计结果。

举例

--考虑表
┌─year─┬─month─┬─day─┐
│ 2019 │     1 │   5 │
│ 2019 │     1 │  15 │
│ 2020 │     1 │   5 │
│ 2020 │     1 │  15 │
│ 2020 │    10 │   5 │
│ 2020 │    10 │  15 │
└──────┴───────┴─────┘

SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH CUBE;

--由于GROUP BY部分具有三个键表达式,因此结果包含八个表,其中包含所有键表达式组合的小计:
--GROUP BY :
--year, month, day
--year, month
--year, day
--year
--month, day
--month
--day
--全部
--从GROUP BY中排除的列将填充零。
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │    10 │  15 │       1 │
│ 2020 │     1 │   5 │       1 │
│ 2019 │     1 │   5 │       1 │
│ 2020 │     1 │  15 │       1 │
│ 2019 │     1 │  15 │       1 │
│ 2020 │    10 │   5 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     1 │   0 │       2 │
│ 2020 │     1 │   0 │       2 │
│ 2020 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2020 │     0 │   5 │       2 │
│ 2019 │     0 │   5 │       1 │
│ 2020 │     0 │  15 │       2 │
│ 2019 │     0 │  15 │       1 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│ 2019 │     0 │   0 │       2 │
│ 2020 │     0 │   0 │       4 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     1 │   5 │       2 │
│    0 │    10 │  15 │       1 │
│    0 │    10 │   5 │       1 │
│    0 │     1 │  15 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     1 │   0 │       4 │
│    0 │    10 │   0 │       2 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   5 │       3 │
│    0 │     0 │  15 │       3 │
└──────┴───────┴─────┴─────────┘
┌─year─┬─month─┬─day─┬─count()─┐
│    0 │     0 │   0 │       6 │
└──────┴───────┴─────┴─────────┘

WITH TOTALS修饰符

如果指定了WITH TOTALS修饰符,则将计算另外的一行。

该行将包含:包含默认值(零或空行)的键列,具有在所有行中计算的值(“total”值)的聚合函数列。

此额外的行仅以JSON *,TabSeparated *和Pretty *格式产生,与其他行分开:

  • 在JSON *格式中,此行作为单独的“总计”字段输出。
  • 在TabSeparated *格式中,该行位于主要结果之后,然后是一个空行(在其他数据之后)。
  • 在Pretty *格式中,该行在主要结果之后作为单独的表输出。
  • 其他格式不可用。

存在HAVING时,可以以不同的方式运行WITH TOTALS。 行为取决于totals_mode设置。

totals处理配置

before_having

默认情况(我在20.8.3版本中默认的是‘after_having_exclusive’)。在这种情况下,“total”是针对所有行(包括未通过HAVING和max_rows_to_group_by传递的行)计算的。

其它选项都是计算通过having的行,并在设置max_rows_to_group_by和group_by_overflow_mode ='any'的情况下得到的结果不同。

after_having_exclusive

在“totals”中不包含未通过“max_rows_to_group_by“限制的行。

after_having_inclusive

在“totals”中包含未通过“ max_rows_to_group_by”限制的行。

after_having_auto

计算通过HAVING限制的行数。如果超过一定数量(默认为50%),在“totals”中包含未通过“ max_rows_to_group_by”限制的行。否则,不包括。

totals_auto_threshold

默认为0.5。 after_having_auto的系数。

如果未使用max_rows_to_group_by和group_by_overflow_mode ='any',则after_having的所有变体都是相同的,您可以使用其中任何一个(例如after_having_auto)。

您可以在子查询中使用WITH TOTALS,包括JOIN子句中的子查询(在这种情况下,将合计各个总计值)。

实施细节

聚合是面向列的DBMS的最重要功能之一,因此,它的实现是ClickHouse最优化的部分之一。 

默认情况下,聚合是使用哈希表在内存中完成的。它有40多个不同方式,可以根据“分组键”数据类型自动选择。

外部存储器中的GROUP BY

可以启用将临时数据转储到磁盘来约束GROUP BY的内存使用。

max_bytes_before_external_group_by参数设定使用RAM的阈值。如果设置为0(默认值),则将其禁用。

使用max_bytes_before_external_group_by时,建议将max_memory_usage设置为大约两倍。

因为聚合有两个阶段:(1)读取数据和形成中间数据;(2)合并中间数据。将数据转储到磁盘只能在第1阶段进行。如果没有转储临时数据,则第2阶段可能需要与第1阶段相同的内存量。

例如,如果将max_memory_usage设置为10000000000,并且您要使用外部聚合,则可以将max_bytes_before_external_group_by设置为10000000000,将max_memory_usage设置为20000000000。触发外部聚合(如果至少有一个临时数据转储)时,最大值RAM消耗仅略大于max_bytes_before_external_group_by。

通过分布式查询处理,可以在远程服务器上执行外部聚合。为了使请求者服务器仅使用少量的RAM,请将distributed_aggregation_memory_efficient设置为1。

当将合并的数据刷新到磁盘时,以及当启用distributed_aggregation_memory_efficiency设置并从远程服务器合并结果时,最多消耗RAM总量的1/256 * the_number_of_threads。

启用外部聚合后,如果数据少于max_bytes_before_external_group_by(即未刷新数据到磁盘),查询的运行速度与没有外部聚合时一样快。 如果刷新了任何临时数据,则运行时间将延长几倍(大约三倍)。

如果在GROUP BY之后有一个带有LIMIT的ORDER BY,则RAM的使用量取决于LIMIT中的数据量,而不是整个表中的数据量。 但是,如果ORDER BY没有LIMIT,需要设置外部排序(max_bytes_before_external_sort)

HAVING子句

允许过滤GROUP BY产生的聚合结果。 它与WHERE子句相似,但是区别在于WHERE在聚合之前执行,而HAVING在聚合之后执行。

可以通过别名引用HAVING子句中SELECT子句的聚合结果。 另外,HAVING子句可以过滤查询结果中未返回的其他聚合的结果。

限制

如果未执行汇总,则无法使用HAVING。 请改用WHERE。

INTO OUTFILE子句

在SELECT查询中添加[INTO OUTFILE filename]子句(其中filename是字符串文字),以将其输出重定向到客户端上的指定文件。

实施细节

  • 此功能在命令行客户端和clickhouse-local中可用。 但是,通过HTTP接口发送的查询将失败。
  • 如果文件名相同的文件已经存在,查询将失败。
  • 默认输出格式为TabSeparated(类似于命令行客户端批处理模式)。

JOIN子句

join通过使用一张或者多张表中的内容,使相同字段相同,构建出一张新表。这是具有SQL支持的数据库中的常见操作,它对应于关系代数联接。 一个表联接的特殊情况通常称为“自联接”。

来自ON子句的表达式和来自USING子句的列称为“连接键”。 除非另有说明,否则join从具有匹配“ join键”的行中生成笛卡尔乘积,这可能会产生比源表多得多的行的结果。

语法

SELECT 
FROM 
[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ASOF] JOIN 
(ON )|(USING ) ...

支持的join类型

支持所有标准SQL JOIN类型:

  • INNER JOIN,仅返回匹配的行。
  • LEFT OUTER JOIN,除了匹配的行外,还返回左表中的不匹配行。
  • RIGHT OUTER JOIN,除了匹配的行外,还返回来自右表的不匹配的行。
  • FULL OUTER JOIN,除了匹配的行,还返回了两个表中不匹配的行。
  • CROSS JOIN,产生整个表的笛卡尔积,未指定“联接键”。

没有指定类型的JOIN表示INNER。 可以安全地省略关键字OUTER。 

CROSS JOIN的另一种语法是在FROM子句中指定多个表,并用逗号分隔。

ClickHouse中可用的其他联接类型:

  • LEFT SEMI JOIN 和 RIGHT SEMI JOIN,“联接键”上的白名单,不产生笛卡尔乘积。
  • LEFT ANTI JOIN 和 RIGHT ANTI JOIN,“联接键”上的黑名单,不产生笛卡尔乘积。
  • LEFT ANY JOIN, RIGHT ANY JOIN 和 INNER ANY JOIN,部分(对于LEFT和RIGHT的相对侧)或全部(对于INNER和FULL)将禁用标准JOIN类型的笛卡尔乘积。
  • ASOF JOINLEFT ASOF JOIN,具有不完全匹配的连接序列。 ASOF JOIN用法下面有说。

设置

可以使用join_default_strictness设置覆盖默认的联接类型。ClickHouse服务器对ANY JOIN操作的行为取决于any_join_distinct_right_table_keys设置。

ASOF JOIN 使用

当需要连接不完全匹配的记录时,ASOF JOIN很有用。

该算法需要表格中的特殊列,列需要满足以下性质:

  • 必须包含有序序列。
  • 可以是以下类型之一:Int,UInt,Float *,Date,DateTime,Decimal *。
  • 不能是JOIN子句中的唯一列。
SELECT expressions_list
FROM table_1
ASOF LEFT JOIN table_2
ON equi_cond AND closest_match_cond
--您可以使用任意数量的相等条件和一个最接近的匹配条件
SELECT count() FROM table_1 ASOF LEFT JOIN table_2 ON table_1.a == table_2.b AND table_2.t <= table_1.t.

支持最接近匹配的条件: >>=<<=.

--ASOF JOIN使用equi_columnX进行相等性联
--ASOF JOIN使用asof_column进行与给定条件(例如table_1.asof_column> = table_2.asof_column)最接近的联接。 
--asof_column列始终是USING子句中的最后一个。

SELECT expressions_list
FROM table_1
ASOF JOIN table_2
USING (equi_column1, ... equi_columnN, asof_column)

举例

ASOF JOIN可以从表1中选择一个时间戳,并从表2中选择最匹配给定条件的事件。如果有相等的,那么相等的是最接近的。

在这个例子中,user_id列可用于相等性联接,而ev_time列可用于最接近匹配项联接。例子中,事件_1_1可以与事件_2_1合并,事件_1_2可以与事件_2_3合并,但事件_2_2无法合并。

     table_1                           table_2
  event   | ev_time | user_id       event   | ev_time | user_id
----------|---------|----------   ----------|---------|----------
              ...                               ...
event_1_1 |  12:00  |  42         event_2_1 |  11:59  |   42
              ...                 event_2_2 |  12:30  |   42
event_1_2 |  13:00  |  42         event_2_3 |  13:00  |   42
              ...                               ...

Join表引擎中不支持ASOF连接。

分布式联接

有两种执行涉及分布式表的联接的方法:

  • 使用普通的JOIN时,查询将发送到远程服务器,在每个服务器上运行子查询,以创建正确的表,并使用该表执行联接。 换句话说,右表是在每个服务器上分别形成的。
  • 当使用GLOBAL ... JOIN时,发送端服务器运行一个子查询,生成一个临时表。临时表被传到每个服务器,并运行查询。

小心使用global,可以了解 Distributed subqueries 

使用建议

空值处理

在联接表时,可能会出现空单元格。 设置join_use_nulls定义ClickHouse如何处理。

如果JOIN键是Nullable类型的字段,那么至少有一个键为NULL的行将不被连接。

句法

在USING中指定的列在两个子查询中必须具有相同的名称,而其他列的名称必须不同。 您可以使用别名来更改子查询中的列名称。

USING子句指定一个或多个要连接的列,以建立这些列的相等性。 设置的列列表不带方括号。 不支持更复杂的连接条件。

语法限制

对于单个SELECT查询中的多个JOIN子句:

  • 只有当整张表被join,而不是子查询被join时,才可以使用 [*]号
  • PREWHERE子句不可用

对于ON,WHERE和GROUP BY子句:

  • 不能在ON,WHERE和GROUP BY子句中使用任意表达式,但是您可以在SELECT子句中定义一个表达式,然后通过别名在这些子句中使用它。

性能

运行JOIN时,与查询的其他阶段有关的执行顺序没有优化。JOIN(在右表中进行搜索)在WHERE中进行过滤之前和聚合之前运行。

每次使用相同的JOIN运行查询时,由于不缓存结果,因此每次查询都要重新执行。为避免这种情况,请使用特殊的Join表引擎。

在某些情况下,使用IN代替JOIN更有效。

如果您需要一个JOIN来与维表连接(这些表是较小的表,其中包含维属性,例如广告活动的名称),则JOIN可能不是很方便,因为每个用户都需要重新访问正确的表 查询。

在这种情况下,应使用“外部词典”功能代替JOIN。 有关更多信息,请参见“外部词典”部分。

内存限制

 默认情况下,ClickHouse使用哈希联接算法。 ClickHouse使用并在RAM中为其创建哈希表。 在达到一定的内存消耗阈值之后,clickhouse将进行合并。

如果需要限制JOIN操作的内存消耗,请使用以下设置:

  • max_rows_in_join — 限制哈希表中的行数。
  • max_bytes_in_join — 限制哈希表中的大小。

当达到任何这些限制时,ClickHouse会按照join_overflow_mode设置的指示进行操作。

举例

SELECT
    CounterID,
    hits,
    visits
FROM
(
    SELECT
        CounterID,
        count() AS hits
    FROM test.hits
    GROUP BY CounterID
) ANY LEFT JOIN
(
    SELECT
        CounterID,
        sum(Sign) AS visits
    FROM test.visits
    GROUP BY CounterID
) USING CounterID
ORDER BY hits DESC
LIMIT 10
┌─CounterID─┬───hits─┬─visits─┐
│   1143050 │ 523264 │  13665 │
│    731962 │ 475698 │ 102716 │
│    722545 │ 337212 │ 108187 │
│    722889 │ 252197 │  10547 │
│   2237260 │ 196036 │   9522 │
│  23057320 │ 147211 │   7689 │
│    722818 │  90109 │  17847 │
│     48221 │  85379 │   4652 │
│  19762435 │  77807 │   7026 │
│    722884 │  77492 │  11056 │
└───────────┴────────┴────────┘

LIMIT子句

LIMIT m允许从结果中选择前m行。

LIMIT n,m允许在跳过前n行之后从结果中选择m行。 LIMIT m OFFSET n语法是等效的。

n和m必须为非负整数。

如果没有用于显式排序结果的ORDER BY子句,则结果行的选择可能是任意的,也可能是不确定的。

LIMIT…WITH TIES修饰符

设置WITH TIES修饰符并指定ORDER BY expr_list时,您将得到结果前n行或n,m行,和所有与这些结果具有相同ORDER BY字段值的行

该修饰符还可以与ORDER BY…WITH FILL修饰符结合使用。

SELECT * FROM (
    SELECT number%50 AS n FROM numbers(100)
) ORDER BY n LIMIT 0,5
SELECT * FROM (
    SELECT number%50 AS n FROM numbers(100)
) ORDER BY n LIMIT 0,5

┌─n─┐
│ 0 │
│ 0 │
│ 1 │
│ 1 │
│ 2 │
└───┘

--添加WITH TIES修饰符

SELECT * FROM (
    SELECT number%50 AS n FROM numbers(100)
) ORDER BY n LIMIT 0,5 WITH TIES

┌─n─┐
│ 0 │
│ 0 │
│ 1 │
│ 1 │
│ 2 │
│ 2 │
└───┘
--因为第6行的字段n与第5行具有相同的值“ 2”

LIMIT BY子句

带有LIMIT n BY expressions子句的查询为每个不同的表达式值选择前n行。 LIMIT BY的键可以包含任意数量的表达式。

ClickHouse支持以下语法变体:

  • LIMIT [offset_value, ]n BY expressions
  • LIMIT n OFFSET offset_value BY expressions

在查询处理期间,ClickHouse选择按排序键排序的数据。

排序键是使用ORDER BY子句显式设置的,也可以作为表引擎的属性隐式设置。

然后,ClickHouse应用LIMIT n BY expressions,并为每种不同的表达式组合返回最多n行。

如果指定了OFFSET,则对于属于不同表达式组合的每个数据块,ClickHouse从块的开头开始跳过offset_value行数,并返回最多n行。 如果offset_value大于数据块中的行数,则ClickHouse从该块返回零行。

LIMIT BY与LIMIT不相关。 它们都可以在同一查询中使用。

举例

--创建一张表
CREATE TABLE limit_by(id Int, val Int) ENGINE = Memory;
INSERT INTO limit_by VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21);
SELECT * FROM limit_by ORDER BY id, val LIMIT 2 BY id
┌─id─┬─val─┐
│  1 │  10 │
│  1 │  11 │
│  2 │  20 │
│  2 │  21 │
└────┴─────┘
SELECT * FROM limit_by ORDER BY id, val LIMIT 1, 2 BY id
┌─id─┬─val─┐
│  1 │  11 │
│  1 │  12 │
│  2 │  21 │
└────┴─────┘
--SELECT * FROM limit_by ORDER BY id, val LIMIT 2 OFFSET 1 BY id也会返回上面结果

--以下查询返回每个域的前5个网址,device_type对,总共最多100行(LIMIT n BY + LIMIT)。
SELECT
    domainWithoutWWW(URL) AS domain,
    domainWithoutWWW(REFERRER_URL) AS referrer,
    device_type,
    count() cnt
FROM hits
GROUP BY domain, referrer, device_type
ORDER BY cnt DESC
LIMIT 5 BY domain, device_type
LIMIT 100

ORDER BY子句

ORDER BY子句包含一个表达式列表,每个表达式都可以使用DESC(降序)或ASC(升序)修饰符来确定排序方向。 如果未指定方向,则采用ASC,因此通常将其省略。

排序方向适用于单个表达式,而不适用于整个列表。 示例:ORDER BY vistss DESC,SearchPhrase

排序表达式列表中具有相同值的行以任意顺序输出,该顺序可能不确定(每次都不同)。

如果省略ORDER BY子句,行的顺序也未定义,返回结果不确定。

特殊值排序

NaN和NULL排序顺序有两种方法:

  • NULLS LAST修饰符:默认情况。首先是值,然后是NaN,然后是NULL。
  • NULLS FIRST修饰符:首先为NULL,然后为NaN,然后为其他值。

举例

┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 2 │    2 │
│ 1 │  nan │
│ 2 │    2 │
│ 3 │    4 │
│ 5 │    6 │
│ 6 │  nan │
│ 7 │ ᴺᵁᴸᴸ │
│ 6 │    7 │
│ 8 │    9 │
└───┴──────┘

SELECT * FROM t_null_nan ORDER BY y NULLS FIRST
┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 7 │ ᴺᵁᴸᴸ │
│ 1 │  nan │
│ 6 │  nan │
│ 2 │    2 │
│ 2 │    2 │
│ 3 │    4 │
│ 5 │    6 │
│ 6 │    7 │
│ 8 │    9 │
└───┴──────┘

对浮点数进行排序时,NaN与其他值分开。 不管排序顺序如何,NaN都会排在最后。 

换句话说,对于升序排序,它们比所有其他数字都大,而对于降序排序,它们比其余数字都小。

大小写支持

对于按字符串值排序,可以指定排序规则(比较)。 举例:ORDER BY SearchPhrase COLLATE 'tr'——使用土耳其字母按升序对关键字进行排序,不区分大小写,假设字符串是UTF-8编码的。

可以为ORDER BY中的每个表达式分别指定或不指定COLLATE。 如果指定了ASC或DESC,则在其后指定COLLATE。 使用COLLATE时,排序始终不区分大小写。

我们仅建议使用COLLATE进行少量行的最终排序,因为使用COLLATE进行排序的效率不如按字节进行常规排序。

实施细节

如果除ORDER BY之外还指定了足够小的LIMIT,则使用较少的RAM。否则,花费的内存量与要排序的数据量成正比。

对于分布式查询处理,如果省略GROUP BY,则在远程服务器上部分完成排序,然后在请求者服务器上合并结果。这意味着对于分布式排序,要排序的数据量可能大于单个服务器上的内存量。

如果没有足够的RAM,则可以在外部存储器中执行排序(在磁盘上创建临时文件)。为此,请设置max_bytes_before_external_sort,如果将其设置为0(默认值),则禁用外部排序。

如果启用,则当要排序的数据量达到指定的字节数时,将对收集的数据进行排序并转储到临时文件中。 读取所有数据后,将合并所有排序的文件并输出结果。 临时文件路径在tmp_path中被设置。

运行查询可能比max_bytes_before_external_sort占用更多的内存。 因此,此设置的值必须明显小于max_memory_usage。 例如,如果您的服务器具有128 GB的RAM,并且您需要运行一个查询,请将max_memory_usage设置为100 GB,将max_bytes_before_external_sort设置为80 GB。

外部排序的工作效率远不及RAM排序。

优化数据读取

如果ORDER BY表达式的前缀与表排序键一致,则可以使用optimize_read_in_order设置来优化查询。

启用optimize_read_in_order设置后,Clickhouse服务器将使用表索引并按照ORDER BY键的顺序读取数据。 这样可以避免在指定LIMIT的情况下读取所有数据。 因此,对具有小限制的大数据的查询将更快地处理。

优化可同时用于ASC和DESC,而不能与GROUP BY子句和FINAL修饰符一起使用。

禁用optimize_read_in_order设置后,Clickhouse服务器在处理SELECT查询时将不使用表索引。

当运行具有ORDER BY子句,较大的LIMIT和WHERE条件(要求在找到查询的数据之前先读取大量记录)的查询时,手动禁用optimize_read_in_order。

以下表引擎支持优化:

  • MergeTree引擎
  • 通过MergeTree引擎表创建的Merge,Buffer和MaterializedView引擎表

在MaterializedView-engine表中,优化可与SELECT ... FROM merge_tree_table ORDER BY pk之类的视图一起使用。 但是,如果SELECT ... FROM view ORDER BY pk之类的查询中不包含ORDER BY子句,则该查询不支持该查询。

ORDER BY Expr WITH FILL修饰符

该修饰符还可以与LIMIT…WITH TIES修饰符结合使用。

可以在ORDER BY expr之后使用可选的FROM expr,TO expr和STEP expr参数设置WITH FILL修饰符。

expr列的所有缺失值将被顺序填充,而其他列将被填充为默认值。

使用以下语法填充多列,然后在ORDER BY部分的每个字段名称后添加WITH FILL修饰符和可选参数。

ORDER BY expr [WITH FILL] [FROM const_expr] [TO const_expr] [STEP const_numeric_expr], ... exprN [WITH FILL] [FROM expr] [TO expr] [STEP numeric_expr]

 

WITH FILL仅适用于数字(所有类型的浮点,十进制,整数)或日期/日期时间类型的字段。

  • 如果未定义FROM const_expr填充序列,则使用ORDER BY中的最小expr字段值
  • 如果未定义TO const_expr填充序列,则使用ORDER BY中的最大expr字段值
  • 如果定义了STEP const_numeric_expr,const_numeric_expr对于数字类型按原样解释,对于日期类型解释为天,对于日期时间类型解释为秒。
  • 当定义STEP const_numeric_expr时,对于数字类型按原样解释,对于日期类型解释为天,对于日期时间类型解释为秒。
  • 当省略STEP const_numeric_expr时,对于数字类型使用1.0,对于日期类型使用1天,对于日期时间类型使用1秒。

举例

SELECT n, source FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n
┌─n─┬─source───┐
│ 1 │ original │
│ 4 │ original │
│ 7 │ original │
└───┴──────────┘

--使用WITH FILL
SELECT n, source FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n WITH FILL FROM 0 TO 5.51 STEP 0.5
┌───n─┬─source───┐
│   0 │          │
│ 0.5 │          │
│   1 │ original │
│ 1.5 │          │
│   2 │          │
│ 2.5 │          │
│   3 │          │
│ 3.5 │          │
│   4 │ original │
│ 4.5 │          │
│   5 │          │
│ 5.5 │          │
│   7 │ original │
└─────┴──────────┘

当有多个order by字段:

SELECT
    toDate((number * 10) * 86400) AS d1,
    toDate(number * 86400) AS d2,
    'original' AS source
FROM numbers(10)
WHERE (number % 3) = 1
ORDER BY
    d2 WITH FILL,
    d1 WITH FILL STEP 5;
┌───d1───────┬───d2───────┬─source───┐
│ 1970-01-11 │ 1970-01-02 │ original │
│ 1970-01-01 │ 1970-01-03 │          │
│ 1970-01-01 │ 1970-01-04 │          │
│ 1970-02-10 │ 1970-01-05 │ original │
│ 1970-01-01 │ 1970-01-06 │          │
│ 1970-01-01 │ 1970-01-07 │          │
│ 1970-03-12 │ 1970-01-08 │ original │
└────────────┴────────────┴──────────┘
SELECT
    toDate((number * 10) * 86400) AS d1,
    toDate(number * 86400) AS d2,
    'original' AS source
FROM numbers(10)
WHERE (number % 3) = 1
ORDER BY
    d1 WITH FILL STEP 5,
    d2 WITH FILL;

┌───d1───────┬───d2───────┬─source───┐
│ 1970-01-11 │ 1970-01-02 │ original │
│ 1970-01-16 │ 1970-01-01 │          │
│ 1970-01-21 │ 1970-01-01 │          │
│ 1970-01-26 │ 1970-01-01 │          │
│ 1970-01-31 │ 1970-01-01 │          │
│ 1970-02-05 │ 1970-01-01 │          │
│ 1970-02-10 │ 1970-01-05 │ original │
│ 1970-02-15 │ 1970-01-01 │          │
│ 1970-02-20 │ 1970-01-01 │          │
│ 1970-02-25 │ 1970-01-01 │          │
│ 1970-03-02 │ 1970-01-01 │          │
│ 1970-03-07 │ 1970-01-01 │          │
│ 1970-03-12 │ 1970-01-08 │ original │
└────────────┴────────────┴──────────┘

OFFSET FETCH修饰符

OFFSET和FETCH允许您按部分检索数据。可以指定数据行块。

OFFSET offset_row_count {ROW | ROWS}] [FETCH {FIRST | NEXT} fetch_row_count {ROW | ROWS} {ONLY | WITH TIES}]

offset_row_count或fetch_row_count值可以是数字或文字常量。 可以省略fetch_row_count; 默认情况下,它等于1。

OFFSET指定在查询返回的行数需要从开始跳过多少行,FETCH指定查询结果中可以包含的最大行数。ONLY选项用于返回紧接在OFFSET省略的行之后的行。

在这种情况下,FETCH是LIMIT子句的替代方法。

举例

--以下两个查询返回结果相同
SELECT * FROM test_fetch ORDER BY a OFFSET 1 ROW FETCH FIRST 3 ROWS ONLY;

SELECT * FROM test_fetch ORDER BY a LIMIT 3 OFFSET 1;

WITH TIES选项用于根据ORDER BY子句返回与结果集中最后一位相关的所有其他行。 例如,如果fetch_row_count设置为5,但另外两行与第五行中ORDER BY列的值匹配,则结果集将包含七行。

根据标准,如果同时存在,OFFSET子句必须在FETCH子句之前。

--考虑表
┌─a─┬─b─┐
│ 1 │ 1 │
│ 2 │ 1 │
│ 3 │ 4 │
│ 1 │ 3 │
│ 5 │ 4 │
│ 0 │ 6 │
│ 5 │ 7 │
└───┴───┘

--使用ONLY
SELECT * FROM test_fetch ORDER BY a OFFSET 3 ROW FETCH FIRST 3 ROWS ONLY;
┌─a─┬─b─┐
│ 2 │ 1 │
│ 3 │ 4 │
│ 5 │ 4 │
└───┴───┘

--使用WITH TIES
SELECT * FROM test_fetch ORDER BY a OFFSET 3 ROW FETCH FIRST 3 ROWS WITH TIES;
┌─a─┬─b─┐
│ 2 │ 1 │
│ 3 │ 4 │
│ 5 │ 4 │
│ 5 │ 7 │
└───┴───┘

PREWHERE子句

Prewhere是一项优化,可以更有效地应用过滤。 即使未显式指定PREWHERE子句,默认情况下也会启用。它自动将WHERE条件的一部分移动到预备阶段来工作。

PREWHERE子句的作用仅是在您认为自己比默认情况下做得更好的情况下控制优化

手动控制前置位置

该子句与WHERE子句具有相同的含义。 

区别在于从表中读取数据的方式不同。 手动控制PREWHERE给少数列使用过滤条件,可提供强大的数据过滤功能。 这减少了读取的数据量。

查询可以同时指定PREWHERE和WHERE。 在这种情况下,PREWHERE在WHERE之前工作。

如果将optimize_move_to_prewhere设置为0,将禁用将部分表达式从WHERE移至PREWHERE。

限制

只适用于MergeTree家族的引擎表。

SAMPLE子句

SAMPLE子句允许进行近似的SELECT查询处理。

启用数据采样后,不会对所有数据执行查询,而是仅对特定部分数据(采样)执行查询。 例如,如果您需要计算所有访问的统计信息,那么只需对所有访问的1/10部分执行查询,然后将结果乘以10就足够了。

在以下场景使用近似查询处理:

  • 如果原始数据不准确,则近似值不会明显降低质量。
  • 业务需求的目标是大致的结果(出于成本效益或将确切结果推销给高级用户的目的)。
  • 如果您有严格的时序要求(例如\ <100ms),但是您无法证明是不是需要额外的硬件资源来满足这些要求。

仅在表创建期间指定了采样表达式的情况下,才可以对MergeTree家族中的表使用采样(请参见MergeTree引擎)。

数据采样的功能如下:

  • 数据采样是确定性机制。 相同的SELECT ..SAMPLE查询的结果始终相同。
  • 对于具有单个采样键的表,具有相同系数的采样始终选择数据的相同子集。
  • 可以在IN子句的子查询中使用样本。 另外,您可以使用JOIN子句加入示例。
  • 采样允许从磁盘读取较少的数据。 但是您必须正确指定采样键。 

使用方法为:

  • SAMPLE K,K是从0到1的实数:SAMPLE 0.1对10%的数据运行查询
  • SAMPLE N,N是正整数:SAMPLE 100,至少在100条数据运行查询
  • AMPLE k OFFSET m:k和m是从0到1的数字。查询是对数据的k个分数样本执行的。 用于样本的数据偏移m分数。

SAMPLE K

--从数据中取10%,因为聚合函数的值不会自动更正,因此要得到一个近似结果,请将count()手动乘以10。
SELECT
    Title,
    count() * 10 AS PageViews
FROM hits_distributed
SAMPLE 0.1
WHERE
    CounterID = 34
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000

SAMPLE N

在此,n是足够大的整数。例如,SAMPLE 10000000。至少在10,000,000行上运行查询。

由于数据读取的最小单位是一个颗粒(其大小由index_granularity设置设置),因此设置一个比颗粒大得多的样本是有意义的。

使用SAMPLE n子句时,您不知道处理了哪些相对百分比的数据。 因此,您不知道聚合函数应乘以的系数。 使用_sample_factor虚拟列来获取近似结果。_sample_factor列包含动态计算的相对系数。 使用指定的采样键创建表时,将自动创建此列。 _sample_factor列的用法示例如下所示。

SELECT sum(PageViews * _sample_factor)
FROM visits
SAMPLE 10000000

SELECT sum(_sample_factor)
FROM visits
SAMPLE 10000000

--平均值不需要使用系数
SELECT avg(Duration)
FROM visits
SAMPLE 10000000

SAMPLE K OFFSET M

SAMPLE 1/10
[++------------]

SAMPLE 1/10 OFFSET 1/2
[------++------]

UNION子句

默认情况下,UNION具有与UNION DISTINCT相同的行为,但是您可以通过设置union_default_mode来指定联合模式,值可以是“ ALL”,“ DISTINCT”或空字符串。

但是,如果您使用UNION并将union_default_mode设置为空字符串,它将引发异常。

UNION ALL

使用UNION ALL进行结果扩展,来结合任意数量的SELECT查询。

SELECT CounterID, 1 AS table, toInt64(count()) AS c
    FROM test.hits
    GROUP BY CounterID

UNION ALL

SELECT CounterID, 2 AS table, sum(Sign) AS c
    FROM test.visits
    GROUP BY CounterID
    HAVING c > 0

结果列按其索引匹配(SELECT内的顺序)。 如果列名不匹配,则从第一个查询中获取最终结果的名称。

对联合执行类型转换。 例如,如果两个查询组合在一起的字段,同时具有是兼容类型的不可为空和可为空类型的相同字段,则所得的UNION ALL将具有可为空的类型字段。

属于UNION ALL的查询不能放在圆括号中。 ORDER BY和LIMIT应用于单独的查询,而不是最终结果。 如果需要对最终结果进行转换,则可以将带有UNION ALL的所有查询放在FROM子句的子查询中。

UNION DISTINCT

UNION ALL和UNION DISTINCT之间的区别在于UNION DISTINCT将对联合结果进行不同的转换,等效于包含UNION ALL的子查询中的SELECT DISTINCT。

实施细节

可以同时运行属于UNION / UNION ALL / UNION DISTINCT的查询,并且可以将它们的结果混合在一起。

WHERE子句

WHERE子句允许筛选来自SELECT的FROM子句的数据。

如果有WHERE子句,则它必须包含UInt8类型的表达式。 这通常是带有比较和逻辑运算符的表达式。 该表达式计算为0的行将从进一步的转换或结果中排除。

如果基础表引擎支持,则将根据使用索引和分区修剪的能力来评估WHERE表达式。

有一个称为prewhere的过滤优化。

WITH子句

Clickhouse支持通用表表达式(CTE),该表提供在SELECT查询中使用WITH子句的结果。

命名子查询可以包含到表对象当前所在的位置和子查询上下文中。

通过从WITH表达式中隐藏当前级别的CTE,可以防止递归。

语法

WITH  AS 
WITH  AS 

举例

--将常量表达式用作“变量”
WITH '2019-08-01 15:23:00' as ts_upper_bound
SELECT *
FROM hits
WHERE
    EventDate = toDate(ts_upper_bound) AND
    EventTime <= ts_upper_bound;

--从SELECT子句列列表中选出一个sum(bytes)表达式结果
WITH sum(bytes) as s
SELECT
    formatReadableSize(s),
    table
FROM system.parts
GROUP BY table
ORDER BY s;

--使用标量子查询的结果
/* this example would return TOP 10 of most huge tables */
WITH
    (
        SELECT sum(bytes)
        FROM system.parts
        WHERE active
    ) AS total_disk_usage
SELECT
    (sum(bytes) / total_disk_usage) * 100 AS table_disk_usage,
    table
FROM system.parts
GROUP BY table
ORDER BY table_disk_usage DESC
LIMIT 10;


--在子查询中重用表达式
WITH test1 AS (SELECT i + 1, j + 1 FROM test1) 
SELECT * FROM test1;

你可能感兴趣的:(clickhouse,数据仓库)