clickHouse之SQL语法之select(—)

  • Post author:
  • Post category:其他


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]

from子句

1、从哪个表、子查询(构建在外部查询的内部,外部没有用到的列将被忽略)、表函数取数据

2、查询中未列出如何的列select count() from test,将额外从表中取些列以计算行数

3、final使查询结果在查询过程中被聚合,仅能用在SELECT from CollapsingMergeTree中

在单个流中读取所有相关的主键列,合并需要的数据(查询变慢,避免使用)

sample子句

1、近似查询,近工作在MergeTree*类型的表中,且建表时指定采样表达式

2、sample子句可用sample k来表示,k可以是0-1的小数值或一个足够大的正整数

k为小数时,查询将使用k做百分比选取数据,sample 0.1只检索数据总量的10%

k足够大的正整数,k为最大样本数,sample 10000000检索最多10000000行数据

取数据总量的0.1 (10%)的数据,查不会自动校正聚合函数最终结果,为更精确结果,count()*10
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

3、相同采样率(建表使用相同采用表达式)得到结果一致

array join子句

1、等同于innert join;使查询与数组和nested类型连接,类似array join函数

2、一个查询只能出现一个array join,where/prewhere使用array join子句,其将优先于where/prewhere子句执行,否则将在where/prewhere子句之后执行

SELECT * FROM arrays_test
┌─s───────┬─arr─────┐
│ Hello   │ [1,2]   │
│ World   │ [3,4,5] │
│ Goodbye │ []      │


SELECT s, arr FROM arrays_test ARRAY JOIN arr
┌─s─────┬─arr─┐
│ Hello │   1 │
│ Hello │   2 │
│ World │   3 │
│ World │   4 │
│ World │   5 │

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 │

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 │

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]             │

join子句

SELECT <expr_list>
FROM <left_subquery>
[GLOBAL] [ANY|ALL] INNER|LEFT|RIGHT|FULL|CROSS [OUTER] JOIN <right_subquery>
(ON <expr_list>)|(USING <column_list>) ...

any与all

all:同SQL的JOIN,右表存在多个与左表关联的数据

any:右表存在多个与左关联的数据,仅返回第一各与左表匹配的数据

global distribution

普通join,查询发送给远程服务器,并在其上生成右表与之关联(右表来自服务器)

使用global join,在请求服务器上计算右表并一

临时表

的方式将其

发送到所有服务器

,每台服务器使用它计算

从子查询中删除所有join不需要的列

执行时无进行执行顺序的优化:join先于where与聚合执行,为显示指定执行顺序,使用子查询方式执行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 │

子查询不允许设置别名或在其他地方引用,

USING

中指定的列必须在两个子查询中具有相同的名称。可通过使用别名来更改子查询的列名(示例中就分别使用了’hits’与’visits’别名)


USING

子句指定要进行链接的一或多个列,系统将列在两表中相等的值连接起来。如果列是一个列表,不需要使用括号包裹。同时JOIN不支持其他更复杂的Join方式,只能在查询中指定一个

JOIN

;若运行多个

JOIN

,你可将它们放入子查询中。

右表(子查询的结果)将会保存在内存中。如果没有足够的内存,则无法运行

JOIN;

每次运行相同的

JOIN

查询,会再次计算, 为避免这情况,可用‘Join’引擎,一个预处理的Join数据结构,总是保存在内存中。更多信息,参见“Join引擎”部分。


在各种类型的JOIN中,最高效的是

ANY LEFT JOIN

,然后是

ANY INNER JOIN

,效率最差的是

ALL LEFT JOIN

以及

ALL INNER JOIN


如果你需要使用

JOIN

来关联一些纬度表(包含纬度属性的一些相对比较小的表,例如广告活动的名称),那么

JOIN

可能不是好的选择,因为语法负责,并且每次查询都将重新访问这些表。对于这种情况,您应该使用“外部字典”的功能来替换

JOIN

。更多信息,参见

外部字典

部分。

null的处理

1、JOIN的行为受

join_use_nulls

的影响。当

join_use_nulls=1

时,

JOIN

的工作与SQL标准相同。

2、如果JOIN的key是

Nullable

类型的字段,则至少一个存在

NULL

值的key不会被关联。

where子句

该子句中须包含一个

UInt8类型的表达式

:通常是一个带有

比较和逻辑

的表达式:在所有数据转换前用来过滤数据;如果在

支持索引

的数据库表

引擎

中,这个表达式将被评估

是否使用索引

PREWHERE 子句

与WHERE子句的意思相同。主要不同在于表数据的读取;PREWHERE

仅支持

*MergeTree


系列引擎

  • 使用PREWHERE,只读取PREWHERE表达式中需要的列,根据PREWHERE执行的结果读取其他需要的列。
  • 如在过滤条件中有少量不适合索引过滤的列,但它们又可提供很强的过滤能力,使用PREWHERE很有意义,帮助减少数据的读取。

例如,在一个需要提取大量列的查询中为少部分列编写PREWHERE是很有作用的。

1、在一个查询中可以同时指定PREWHERE和WHERE,在这种情况下,

PREWHERE优先于WHERE执行

2、PREWHERE

不适合用于已经存在于索引中的列:

列已经存在于索引中,只有满足索引的数据块才会被读取。

3、如将’optimize_move_to_prewhere’设置为1,并且在查询中不包含PREWHERE,则系统将自动的把适合PREWHERE表达式的部分从WHERE中抽离到PREWHERE中。

group by

必含一个表达式列表,每个表达式将被称之为“key”。 SELECT,HAVING,ORDER BY子句中的表达式列表必须来自于这些“key”或聚合函数。

被选择的列中不能包含非聚合函数或key之外的其他列

如查询表达式中仅含聚合函数,则可省略GROUP BY,这时会假定将所有数据聚合成一组空“key”。

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

1、与SQL标准不同,如表中不存在任何数据(表不存在任何数据,或被WHERE过滤掉了),将返回一个空结果,而不是一个包含聚合函数初始值的结果。

2、与MySQL不同(实际上这是符合SQL标准的),不能获得一个不在key中的非聚合函数列(除了常量表达式),但可使用‘any’(返回遇到的第一个值)、max、min等聚合函数使它工作

ELECT
    domainWithoutWWW(URL) AS domain,
    count(),
    any(Title) AS title -- getting the first occurred page header for each domain.
FROM hits
GROUP BY domain

GROUP BY子句会为遇到的每一个不同的key计算一组聚合函数的值

在GROUP BY子句中不使用Array类型的列

常量不能作为聚合函数的参数传入聚合函数中,如sum(1),这种情况下你可以省略常量:count()
┌─x─┬────y─┐
│ 1 │    2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │    2 │
│ 3 │    3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘
┌─sum(x)─┬────y─┐
│      4 │    2 │
│      3 │    3 │
│      5 │ ᴺᵁᴸᴸ │
└────────┴──────┘
再向

GROUP BY

中放入几个key,结果将列出所有的组合可能
SELECT sum(x), y FROM t_null_big GROUP BY y

null处理

对于GROUP BY子句,ClickHouse将

NULL

解释为一个值,并且支持

NULL=NULL

WITH TOTALS 修饰符

  • 将会在结果中得到一个被额外计算出的行,这一行中将包含所有key的默认值(零或者空值),以及所有聚合函数对所有被选择数据行的聚合结果;该行仅在JSON*, TabSeparated*, Pretty*输出格式中与其他行分开输出。

在JSON*输出格式中,这行将出现在Json的‘totals’字段中。在TabSeparated*输出格式中,这行将位于其他结果之后,同时与其他结果使用空白行分隔。在Pretty*输出格式中,这行将作为单独的表在所有结果之后输出。



  • WITH TOTALS

    与HAVING子句同时存在,它的行为受‘totals_mode’配置的影响。 默认情况下,

    totals_mode = 'before_having'

    ,这时

    WITH TOTALS

    将会在HAVING前计算最多不超过

    max_rows_to_group_by

    行的数据。


  • group_by_overflow_mode = 'any'

    并指定了

    max_rows_to_group_by

    的情况下,

    WITH TOTALS

    的行为受

    totals_mode

    的影响。

  • after_having_exclusive

    – 在HAVING后进行计算,计算不超过

    max_rows_to_group_by

    行的数据。

  • after_having_inclusive

    – 在HAVING后进行计算,计算不少于

    max_rows_to_group_by

    行的数据。

  • after_having_auto

    – 在HAVING后进行计算,采用统计通过HAVING的行数,在超过不超过‘max_rows_to_group_by’指定值(默认为50%)的情况下,包含所有行的结果。否则排除这些结果。

  • totals_auto_threshold

    – 默认 0.5,是

    after_having_auto

    的参数。
  • 如果

    group_by_overflow_mode != 'any'

    并没有指定

    max_rows_to_group_by

    情况下,所有的模式都与

    after_having

    相同。

你可以在子查询,包含子查询的JOIN子句中使用WITH TOTALS(在这种情况下,它们各自的总值会被组合在一起)

GROUP BY 使用外部存储设备

  • 你可以在GROUP BY中允许将临时数据转存到磁盘上,以限制对内存的使用。

    max_bytes_before_external_group_by

    这个配置确定了在GROUP BY中启动将临时数据转存到磁盘上的内存阈值。如果你将它设置为0(这是默认值),这项功能将被禁用。
  • 当使用

    max_bytes_before_external_group_by

    时,建议将max_memory_usage设置为它的两倍。因为一个聚合需两个阶段完成:(1)读取数据并形成中间数据 (2)合并中间数据。临时数据的转存只会发生在第一个阶段。如果没有发生临时文件的转存,那么阶段二将最多消耗与1阶段相同的内存大小。

如果将

max_memory_usage

设置为10000000000并且你想要开启外部聚合,那需将

max_bytes_before_external_group_by

=10000000000,将

max_memory_usage

=20000000000。当外部聚合被触发时(如果刚好只形成了一份临时数据),它的内存使用量将会稍高于

max_bytes_before_external_group_by

在分布式查询处理中,

外部聚合将会在远程的服务器中执行

。为了使请求服务器只使用较少的内存,可以设置


distributed_aggregation_memory_efficient

为1

当合并被刷到磁盘的临时数据以及合并远程的服务器返回的结果时,如在启动

distributed_aggregation_memory_efficient

的情况下,将消耗1/256 * 线程数的总内存大小。

当启动外部聚合时,如果数据的大小小于

max_bytes_before_external_group_by

设置的值(数据没有被刷到磁盘中),那么数据的聚合速度将会和没有启动外部聚合时一样快。如果有临时数据被刷到了磁盘中,那么这个查询的运行时间将会被延长几倍(大约是3倍)。


如你在GROUP BY后面存在ORDER BY子句,并且ORDER BY后面存在一个极小限制的LIMIT,那么ORDER BY子句将不会使用太多内存。 否则请不要忘记启动外部排序(

max_bytes_before_external_sort

)。

注意:

如查询中不包含distinct、group by、order by及in、join子查询,将使用O(1)数量的内存完成,否则将消耗大量内存,除非设置了

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


https://clickhouse.yandex/docs/zh/query_language/select/