clickhouse SQL查询语句 【译自Github 英文文档】

内容有缩减,原文请点击这里


创建数据库

CREATE DATABASE [IF NOT EXISTS] db_name


创建表

CREATE TABLE可以有几种形式。

创建一张表,最好指定引擎结构

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = engine

创建一张像另一张表结构的表

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

创建一张自定义引擎,数据及结构来源于另一张表的表

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ...


默认值

列描述可以指定一个默认值的表达式,其中有以下几个方法:默认的expr、物化expr、别名expr。示例:URLDomain字符串默认域(URL)。
如果没有定义默认值的表达式,那么默认值将被设置为0,用于数字、字符串的空字符串、数组的空数组,以及用于日期的00-00 00 00 00 00 0000-0000。不支持null。


临时表

在所有情况下,如果指定临时表,就会创建临时表。临时表有以下特点:

  • 临时表在会话结束时消失,包括连接丢失。
  • 一个临时表是用内存引擎创建的。其他表引擎不受支持。
  • DB不能为临时表指定。它是在数据库之外创建的。
  • 如果临时表与另一个表的名称相同,并且查询指定表名而不指定DB,则使用临时表。
  • 对于分布式查询处理,查询中使用的临时表被传递给远程服务器。

在大多数情况下,临时表不是手动创建的,而是在使用外部数据进行查询时,或者在分布式(全局)中使用。

创建视图

CREATE [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]name [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...

有两种类型的视图:正常的和物化的。

在创建物化视图时,必须指定引擎—用于存储数据的表引擎。

物化视图的工作原理如下:当将数据插入SELECT中指定的表时,插入数据的一部分被这个SELECT查询转换,结果被插入到视图中。

普通视图不存储任何数据,只是执行从另一个表读取的数据。换句话说,正常的视图只不过是一个保存的查询。从视图读取时,这个保存的查询被用作FROM子句中的子查询。

举例,假设你已经创建了一个视图

CREATE VIEW view AS SELECT ...

而且写了一个查询

SELECT a, b, c FROM view 

这个查询等价于这个子查询

SELECT a, b, c FROM (SELECT ...)

物化视图是这样安排的:在将数据插入到SELECT中指定的表时,插入数据的一部分被这个SELECT查询转换,结果被插入到视图中。如果您指定填充,则在创建时将现有的表数据插入视图中,就像创建一个CREATE TABLE。作为选择……否则,查询只包含创建视图后插入到表中的数据。我们不建议使用填充,因为在视图创建期间插入到表中的数据不会被插入到其中。

ATTACH

这个查询与CREATE完全相同,但是不是“创建”这个词,它使用“附加”这个词。

该查询不会在磁盘上创建数据,但是假设数据已经在适当的位置,并且只向服务器添加关于表的信息。执行附加查询后,服务器将知道表的存在。如果表以前是分离的,也就是说它的结构是已知的,那么您可以使用速记而不用定义结构。

ATTACH TABLE [IF NOT EXISTS] [db.]name
在启动服务器时使用该查询。服务器将表元数据存储为带有附加查询的文件,它只是在启动时运行(除了在服务器上显式创建的系统表之外)


DETACH

从服务器删除关于“name”表的信息。服务器停止知道表的存在。

DETACH TABLE [IF EXISTS] [db.]name
这不会删除表的数据或元数据。在下一次服务器启动时,服务器将读取元数据并再次查找表。类似地,可以使用附加查询重新连接一个“分离”表(除了系统表之外,没有为它们存储的元数据)。

DROP

DROP数据库

DROP DATABASE [IF EXISTS] db [ON CLUSTER cluster] 

DROP TABLE

DROP [TEMPORARY] TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]

RENAME

重命名一个或多个表。

RENAME TABLE [db11.]name11 TO [db12.]name12, [db21.]name21 TO [db22.]name22, ... [ON CLUSTER cluster]

所有表都在全局锁定下重命名。重命名表是一项轻操作。如果您在之后指定了另一个数据库,那么该表将被移动到这个数据库。但是,带有数据库的目录必须位于相同的文件系统中(否则,将返回错误)


ALTER

ALTER查询只支持MergeTree表,以及mergeand分布式。该查询有几个变体。

ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|MODIFY COLUMN ...

增加列(排在某个列之后)

ADD COLUMN name [type] [default_expr] [AFTER name_after]

删除列

DROP COLUMN name

改变列类型

MODIFY COLUMN name [type] [default_expr]

不支持在主键或采样键中删除列(在引擎表达式中是列)。更改主键中包含的列的类型只有在此更改不会导致数据被修改时才可能(例如,允许将值添加到枚举中,或者将DateTime的类型更改为UInt32)。

有几个处理阶段:

  • 用修改过的数据准备临时的(新的)文件
  • 重命名旧文件
  • 将临时(新)文件重命名为旧的名称
  • 删除旧的文件
只有第一阶段需要时间。如果在此阶段出现故障,则数据不会更改。如果在一个连续的阶段出现故障,则可以手动恢复数据。唯一的例外是,如果旧文件从文件系统中删除,但是新文件的数据没有写到磁盘上,并且丢失了。


分区操作

它只适用于MergeTree的表:
  • DETACH PARTITION —— 将一个分区移动到“分离”目录并忘记它
  • DROP PARTITION —— 删除一个分区
  • ATTACH PART|PARTITION ——  将一个新的部分或分区从分离的目录添加到表
  • FREEZE PARTITION —— 创建一个分区的备份
  • FETCH PARTITION  —— 从另一个服务器下载一个分区

每一种类型的查询都被单独地覆盖。

表中的一个分区是单个日历月的数据。这是由表引擎参数中指定的日期键的值决定的。每个月的数据都是单独存储的,以简化对这些数据的操作。

表中的“部分”是来自单个分区的数据的一部分,按主键排序你可以使用这个系统。用于查看表部分和分区的部件表

例如:只算活跃的部分,不活跃的部分是在合并到更大的部分后剩余的部分——这些部分在合并后大约10分钟被删除。

SELECT * FROM system.parts WHERE active

另一种查看一组部件和分区的方法是使用表数据进入目录。数据目录:/var/lib/clickhouse/data/database/table/,/var/lib/clickhouse/ ClickHouse的路径数据,数据库的数据库名称,表的表名。

$ ls -l /var/lib/clickhouse/data/test/visits/
total 48
drwxrwxrwx 2 clickhouse clickhouse 20480 may   13 02:58 20140317_20140323_2_2_0
drwxrwxrwx 2 clickhouse clickhouse 20480 may   13 02:58 20140317_20140323_4_4_0
drwxrwxrwx 2 clickhouse clickhouse  4096 may   13 02:55 detached
-rw-rw-rw- 1 clickhouse clickhouse     2 may   13 02:58 increment.txt

在这里,2014031720140323220和20140320140323440是数据部分的目录。让我们来分解第一部分的名

  • 20140317是数据块中数据的最小日期
  • 20140323是数据块中数据的最大日期
  • 2是数据块的最小值
  • 2是数据块的最大值
  • 0是块级别(合并树的深度是由它形成的)

每个部分都与一个单独的分区有关,并且只包含一个月的数据。201403是分区的名称。一个分区是一个月的一组部件。

在操作服务器上,您不能手动更改文件系统上的部分或它们的数据,因为服务器不知道它。对于非复制表,当服务器停止时,您可以这样做,但我们不建议这样做。对于复制的表,在任何情况下都不能更改部分。

分离的目录包含服务器不使用的部分——使用ALTER从表中分离

将名为“name”的分区的所有数据移动到“分离”目录,然后忘记它们。分区名称以YYYYMM格式指定。它可以用单引号或没有引号表示

ALTER TABLE [db.]table DETACH PARTITION 'name'

与分离操作相同。从表中删除数据。数据部分将被标记为不活动,在大约10分钟内将被完全删除。该查询将被复制——数据将在所有副本上被删除。

ALTER TABLE [db.]table ATTACH PARTITION|PART 'name'

从“分离”目录中添加数据到表中。

ALTER TABLE [db.]table ATTACH PARTITION|PART 'name'

创建一个或多个分区的本地备份。名称可以是分区的全名(例如,201403),或者它的前缀(例如,2014):然后将为所有相应的分区创建备份。

ALTER TABLE [db.]table FREEZE PARTITION 'name'


备份和复制

这个查询只适用于可复制的表

ALTER TABLE [db.]table FETCH PARTITION 'name' FROM 'path-in-zookeeper'

尽管查询被称为ALTER TABLE,但它不会改变表结构,也不会立即更改表中可用的数据。它从碎片中下载指定的分区,它在FROM子句中指定了路径,然后将其放入指定表的分离目录中。


SHOW DATABASES

展示所有数据库

SHOW DATABASES [INTO OUTFILE filename] [FORMAT format]

SHOW TABLES

展示数据库里所有表格

SHOW [TEMPORARY] TABLES [FROM db] [LIKE 'pattern'] [INTO OUTFILE filename] [FORMAT format]

SHOW PROCESSLIST

输出正在处理的查询列表

SHOW PROCESSLIST [INTO OUTFILE filename] [FORMAT format]

SHOW CREATE TABLE

显示创建表的SQL

DESC|DESCRIBE TABLE [db.]table [INTO OUTFILE filename] [FORMAT format]

DESCRIBE TABLE

每个字段的名称及类型

DESC|DESCRIBE TABLE [db.]table [INTO OUTFILE filename] [FORMAT format]

EXISTS

存在一个utf-8的列,返回1,否则0

EXISTS [TEMPORARY] TABLE [db.]name [INTO OUTFILE filename] [FORMAT format]

USE

使用某一个数据库

USE db

SET

SET param = value

允许您设置param值,如果指定了全局,则为会话或服务器(全局)设置设置。在创建全局设置时,设置并不适用于已经运行的会话,包括当前会话。它只会用于新会话。当服务器重新启动时,使用SET的全局设置就会丢失。要在服务器重新启动后进行设置

OPTIMIZE

OPTIMIZE TABLE [db.]name [PARTITION partition] [FINAL]

仅支持MergeTree引擎, 用于回收闲置的数据库空间,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排

INSERT 

不严格插入数据,没有出现的列自动填充为默认值

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

严格插入数据,每一列都必须出现在上面

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

插入select 得到的数据

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

Performance considerations

按主键对输入数据进行排序,并按月将它们划分为分区。如果您在混合月份插入数据,那么它会显著降低INSERT查询的性能。为了避免这种情况:

  • 在相当大的批量中添加数据,比如一次100,000行。
  • 在将数据上传至ClickHouse之前,先将数据分组。

性能不会降低:

  • 数据是实时添加的
  • 上传的数据通常是按时间排序的

SELECT

SELECT [DISTINCT] expr_list
    [FROM [db.]table | (subquery) | table_function] [FINAL]
    [SAMPLE sample_coeff]
    [ARRAY JOIN ...]
    [GLOBAL] ANY|ALL INNER|LEFT JOIN (subquery)|table USING columns_list
    [PREWHERE expr]
    [WHERE expr]
    [GROUP BY expr_list] [WITH TOTALS]
    [HAVING expr]
    [ORDER BY expr_list]
    [LIMIT [n, ]m]
    [UNION ALL ...]
    [INTO OUTFILE filename]
    [FORMAT format]
    [LIMIT n BY columns]

子句几乎与查询执行传输器的顺序相同。

在使用FINAL时,查询处理得比较慢。在大多数情况下,您应该避免使用FINAL。


SAMPLE

仅支持MergeTree引擎,且必须在创建时指定抽样表达式

SELECT
    Title,
    count() * 10 AS PageViews
FROM hits_distributed
SAMPLE 0.1
WHERE
    CounterID = 34
    AND toDate(EventDate) >= toDate('2013-01-29')
    AND toDate(EventDate) <= toDate('2013-02-04')
    AND NOT DontCountHits
    AND NOT Refresh
    AND Title != ''
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000

SAMPLE k 当k在0~1之间时,如上例k=0.1,即在10%上的数据执行

SAMPLE k 当k为正整数时,如上例k=1000,运行该查询最多为1000行

上例是在10%的数据里执行的,所以count()要手动*10当使用类似10000000的样例时,没有任何关于哪个相对百分比的数据被处理的信息,或者聚合函数应该乘以什么,因此这种写作方法并不总是适用于这种情况。


ARRAY JOIN

查询只能指定单个数组连接子句

ARRAY本质上和INNER JOIN 一样,举例:

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

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

Ok.

0 rows in set. Elapsed: 0.001 sec.

:) INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', [])

INSERT INTO arrays_test VALUES

Ok.

3 rows in set. Elapsed: 0.001 sec.

:) SELECT * FROM arrays_test

SELECT *
FROM arrays_test

┌─s───────┬─arr─────┐
│ Hello   │ [1,2]   │
│ World   │ [3,4,5] │
│ Goodbye │ []      │
└─────────┴─────────┘

3 rows in set. Elapsed: 0.001 sec.

:) SELECT s, arr FROM arrays_test ARRAY JOIN arr

SELECT s, arr
FROM arrays_test
ARRAY JOIN arr

┌─s─────┬─arr─┐
│ Hello │   1 │
│ Hello │   2 │
│ World │   3 │
│ World │   4 │
│ World │   5 │
└───────┴─────┘

5 rows in set. Elapsed: 0.001 sec.

可以在数组连接子句中指定一个别名。在这种情况下,可以通过这个别名访问数组项

:) SELECT s, arr, a FROM arrays_test ARRAY JOIN arr AS a

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 │
└───────┴─────────┴───┘

5 rows in set. Elapsed: 0.001 sec.

相同大小的多个数组可以在数组连接子句中进行逗号分隔。在这种情况下,连接是同时执行的

:) 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

SELECT s, arr, a, num, mapped
FROM arrays_test
ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(lambda(tuple(x), plus(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 │
└───────┴─────────┴───┴─────┴────────┘

5 rows in set. Elapsed: 0.002 sec.

:) SELECT s, arr, a, num, arrayEnumerate(arr) FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num

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]             │
└───────┴─────────┴───┴─────┴─────────────────────┘

5 rows in set. Elapsed: 0.002 sec.

数组连接也可以使用嵌套的数据结构

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

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

Ok.

0 rows in set. Elapsed: 0.006 sec.

:) INSERT INTO nested_test VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], [])

INSERT INTO nested_test VALUES

Ok.

3 rows in set. Elapsed: 0.001 sec.

:) SELECT * FROM nested_test

SELECT *
FROM nested_test

┌─s───────┬─nest.x──┬─nest.y─────┐
│ Hello   │ [1,2]   │ [10,20]    │
│ World   │ [3,4,5] │ [30,40,50] │
│ Goodbye │ []      │ []         │
└─────────┴─────────┴────────────┘

3 rows in set. Elapsed: 0.001 sec.

:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest

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 │
└───────┴────────┴────────┘

5 rows in set. Elapsed: 0.001 sec.

当在数组连接中指定嵌套数据结构的名称时,其含义与数组连接的含义相同,它包含了所有数组元素。

如下SQL与上面结果相同:

:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x, nest.y

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 │
└───────┴────────┴────────┘

5 rows in set. Elapsed: 0.001 sec.

其他示例:

:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x

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] │
└───────┴────────┴────────────┘

5 rows in set. Elapsed: 0.001 sec.

别名依旧可以用于ARRAY:

:) SELECT s, n.x, n.y, nest.x, nest.y FROM nested_test ARRAY JOIN nest AS n

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] │
└───────┴─────┴─────┴─────────┴────────────┘

5 rows in set. Elapsed: 0.001 sec.

使用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

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 │
└───────┴─────┴─────┴─────────┴────────────┴─────┘

5 rows in set. Elapsed: 0.002 sec.

JOIN

该连接与上面描述的array join 没有关联

[GLOBAL] ANY|ALL INNER|LEFT [OUTER] JOIN (subquery)|table USING columns_list

有几种类型的连接:

INNER or LEFT:同mysql

ANY or ALL:如果指定了ANY,那么只有第一个找到的行被连接,如果指定了ALL,那么所有符合的行都被连接

GLOBAL ... JOIN:全局查询,首先,请求者服务器运行一个子查询来计算正确的表。这个临时表被传递给每个远程服务器,并使用传输的临时数据在它们上运行查询,请谨慎使用GLOBAL ... JOIN

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 │
└───────────┴────────┴────────┘
  子查询不允许您设置名称或使用它们来引用特定子查询中的列。在使用中指定的列必须在子查询中具有相同的名称,而其他列必须以不同的方式命名。您可以使用别名来更改子查询中的列的名称

右表(子查询结果)驻留在RAM中。如果没有足够的内存,就不能运行一个连接。

只有一个连接可以在查询中指定(在一个级别上)。要运行多个连接,您可以将它们放入子查询中

在各种类型的连接中,最有效的是任何左连接,然后是任何内部连接。最低效的是所有的左连接和所有内部连接。

如果需要连接维度表(这些表中包含维度属性的相对较小的表,例如用于广告活动的名称),那么连接可能不太方便,因为使用了大量的语法,而且每个查询都需要重新访问正确的表。对于这种情况,有一个“外部字典”特性,您应该使用它而不是连接


WHERE

如果有WHERE子句,它必须包含带有UInt8类型的表达式

PREWHERE

首先,只需要执行预读操作的列。然后读取其他列来运行查询,但是只有那些块才是真值仅支持引擎为 MergeTree的表


GROUP BY

SELECT
    count(),
    median(FetchTiming > 60 ? 60 : FetchTiming),
    count() - sum(Refresh)
FROM hits

与MySQL不同(并且符合标准SQL),您不能获得某些不在键或聚合函数中的列的值(除非是常量表达式)。为了解决这个问题,您可以使用“any”聚合函数(获得第一个碰见的值)或“最小/最大值”。

SELECT
    domainWithoutWWW(URL) AS domain,
    count(),
    any(Title) AS title -- getting the first occurred page header for each domain.
FROM hits
GROUP BY domain
一个常量不能被指定为聚合函数的参数,你可以消去常数。例如用count()代替sum(1)。


WITH TOTALS modifier



GROUP BY in external memory

您可以将临时数据转储到磁盘,以限制组内的内存使用。通过设置,maxbytes先于外部组通过临时数据对文件系统来确定倾倒组的阈值RAM消耗量。如果设置为0(缺省值),则禁用它。


LIMIT N BY

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
这个查询将为每个域选择前5个引用器,devicetype对,但是不会超过100行(限制n BY+限制)。


HAVING

类似where字句,如果没有使用聚合,则不能使用它


ORDER BY

默认为ASC升序,外部排序比RAM排序有效


DISTINCT

Distinct与Group by 不同之处:

  • distinct可以和group by 合用
  • 当Limit被定义并且没有Order by时,在读取了所需数目的不同行之后,查询立即停止运行。
  • 数据块是在处理它们的过程中产生的,而不需要等待整个查询完成运行。

在至少有一个数组存在的情况下,distinct是不被支持的


LIMIT

如果没有一个ORDER BY子句显式地对结果进行排序,那么结果可能是任意的和不确定的


UNION ALL

你可以使用UNION ALL来合并任意数量的查询

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
只支持UNION ALL 不支持UNION

可能列名有所不同,但最终列名取自第一个查询


INTO OUTFILE

添加到OUTFILE filename子句(其中文件名是一个字符串文字)将查询输出重定向到指定的文件。与MySQL相反,文件是在客户端创建的。如果具有相同文件名的文件已经存在,那么查询将失败。这个功能在命令行客户端和clicklocal本地(通过HTTP接口发送的查询将失败)

默认的输出格式是TAB分离的

FORMAT

指定格式“格式”以获取任何指定格式的数据。您可以将其用于方便,或用于创建转储。要了解更多信息,请参阅“格式”一节。如果省略了格式子句,则使用默认的格式,这取决于用于访问DB的设置和接口。对于HTTP接口和批处理模式下的命令行客户端,默认的格式是制表符。对于交互式模式下的命令行客户端,缺省格式是PrettyCompact(它有吸引和紧凑的表)


IN

操作符的左边要么是一个列,要么是一个元祖

SELECT UserID IN (123, 456) FROM ...
SELECT (CounterID, UserID) IN ((34, 123), (101500, 456)) FROM ...
如果右边是一系列常量并且数量不太多(小于数百万),则会使用索引。如果数量太多,会使用子查询


子查询可以为过滤元组指定多个列

SELECT (CounterID, UserID) IN (SELECT CounterID, UserID FROM ...) FROM ...

左右两边列应该具有相同的类型

查询操作符和子查询可以发生在查询的任何部分,包括聚合函数和lambda函数

SELECT
    EventDate,
    avg(UserID IN
    (
        SELECT UserID
        FROM test.hits
        WHERE EventDate = toDate('2014-03-17')
    )) AS ratio
FROM test.hits
GROUP BY EventDate
ORDER BY EventDate Asc

┌──EventDate─┬────ratio─┐
│ 2014-03-17 │        1 │
│ 2014-03-18 │ 0.807696 │
│ 2014-03-19 │ 0.755406 │
│ 2014-03-20 │ 0.723218 │
│ 2014-03-21 │ 0.697021 │
│ 2014-03-22 │ 0.647851 │
│ 2014-03-23 │ 0.648416 │
└────────────┴──────────┘


Distributed subqueries

分布式子查询

in/join 与GLOBAL in /GLOBAL join 的区别:

当使用in的时候,查询被发送到远程服务器,并且每个服务器都在IN或JOIN子句中运行子查询


SELECT uniq(UserID) FROM distributed_table

将会被发送到所有远程服务器

SELECT uniq(UserID) FROM local_table
然后并行运行,直到达到中间结果可以结合的阶段。然后,中间结果将被返回给请求者服务器并在其上合并,最终的结果将被发送到客户端。


两个受众交集的计算:

SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)

这个查询将发送到所有服务器

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)

换句话说, In子句中的数据集将在每个服务器上独立地收集,只收集本地储存的数据。

如果您已经准备好了这种情况,并且在集群服务器上分布数据,那么单个UserID的数据完全驻留在一台服务器上,那么这将是正确和最优的。在这种情况下,所有必需的数据将在每个服务器上本地可用。否则,结果将是不准确的。我们将该查询的变体称为“localin”

当数据在集群服务器上随机分布时,为了纠正查询的工作方式,您可以在子查询中指定分布表。这个查询看起来是这样的:

SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

这个查询将被发送到所有远程服务器

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

子查询将开始在每个远程服务器上运行。由于子查询使用分布式表,所以每个远程服务器上的子查询将会对每个远程服务器都感到不满,如果您有一个100个服务器集群,执行整个查询将需要10000个基本请求,这通常被认为是不可接受的

SELECT UserID FROM local_table WHERE CounterID = 34

在这种情况下,您应该始终使用GLOBAL in 而不是In。让我们看看它是如何在查询中工作的

SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID GLOBAL IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)

服务器将运行子查询

SELECT UserID FROM distributed_table WHERE CounterID = 34

结果将被放在RAM中的临时表中。然后请求将被发送到每个远程服务器

SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID GLOBAL IN _data1
临时表“data1”将连同查询一起被发送到每个远程服务器(临时表的名称是实现定义的)。


记住以下几点:

  • 在创建临时表时,数据不是惟一的。为了减少通过网络传输的数据量,在子查询中指定不同的数据。在 in 的情况下不用
  • 临时表将被发送到所有远程服务器。传输不考虑网络拓扑。例如,如果10个远程服务器驻留在与请求者服务器相对较远的数据中心中,那么数据将通过通道发送10次到远程数据中心。在使用global in时尽量避免大数据集
  • 在将数据传输到远程服务器时,对网络带宽的限制是不可配置的。你可能会让网络超负荷
  • 尝试跨服务器分发数据,这样您就不需要经常使用global in
  • 如果您需要经常使用global in,那么就计划一下ClickHouse集群的位置,这样单个副本就可以驻留在一个数据中心中,并且在它们之间建立一个快速网络,这样一个查询就可以在一个数据中心内进行处理了


Extreme values

极值

除了结果之外,您还可以获得结果列的最小值和最大值。要做到这一点,将extremes设置为1。对于数值类型、日期和日期,计算最小值和最大值。对于其他栏目,输出的是默认值


Notes

笔记,说明

group by 和order by 不支持位置参数,比如group by 1,2不是按第一列第二列聚集,而会被解释为常量分组,即所有行聚合为1

你可以在查询的任何部分加上一个*而不是表达式,但只有少部分*是合理的:

  • 创建表转储时
  • 对于仅包含几个列的表,如系统表
  • 为了获取关于表中有哪些列。在这种情况下,设置limit 1,最好使用DESC查询。
  • 当在少量的列上使用强过滤时 prewhere
  • 在子查询中(因为外部查询不需要的列被排除在子查询之外)。


KILL QUERY

KILL QUERY
  WHERE 
  [SYNC|ASYNC|TEST]
  [FORMAT format]

尝试终止当前正在运行的查询。从系统中选择terminate查询

例如:

-- Terminates all queries with the specified query_id.
KILL QUERY WHERE query_id='2-857d-4a57-9ee0-327da5d60a90'

-- Synchronously terminates all queries run by `username`.
KILL QUERY WHERE user='username' SYNC
只读用户只能终止他们自己的请求







你可能感兴趣的:(clickhouse)