ClickHouse/docs/zh/sql-reference/statements/select.md
BayoNet 4ec7821fec
DOCS-624: Fixing links to nowhere 2 (#10703)
* enbaskakova-DOCSUP-652 (#101)

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

* Update docs/en/sql_reference/aggregate_functions/combinators.md

Co-Authored-By: BayoNet <da-daos@yandex.ru>

* Update docs/en/sql_reference/aggregate_functions/combinators.md

Co-Authored-By: BayoNet <da-daos@yandex.ru>

* Update docs/en/sql_reference/aggregate_functions/combinators.md

Co-Authored-By: BayoNet <da-daos@yandex.ru>

* Update docs/en/sql_reference/aggregate_functions/combinators.md

Co-Authored-By: BayoNet <da-daos@yandex.ru>

* Update docs/en/sql_reference/aggregate_functions/combinators.md

Co-Authored-By: BayoNet <da-daos@yandex.ru>

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

* "docs(orNull&orDefault): Functions 'orNull&orDefault' have been edited"

Co-authored-by: elenbaskakova <elenbaskakova@yandex-team.ru>
Co-authored-by: BayoNet <da-daos@yandex.ru>

* Revert "enbaskakova-DOCSUP-652 (#101)" (#107)

This reverts commit 639fee7610.

* CLICKHOUSEDOCS-624: Fixed en

* CLICKHOUSEDOCS-624: Fixed RU.

* CLICKHOUSEDOCS-624: Fixed ES and FR.

* CLICKHOUSEDOCS-624: Fixed JA and TR

* CLICKHOUSEDOCS-624: Fixed FA.

* CLICKHOUSEDOCS-624: Fixed ZH.

* Update docs/tools/test.py

Co-authored-by: Ivan Blinkov <github@blinkov.ru>

* Update docs/tools/test.py

Co-authored-by: Ivan Blinkov <github@blinkov.ru>

Co-authored-by: elenaspb2019 <47083263+elenaspb2019@users.noreply.github.com>
Co-authored-by: elenbaskakova <elenbaskakova@yandex-team.ru>
Co-authored-by: Sergei Shtykov <bayonet@yandex-team.ru>
Co-authored-by: Ivan Blinkov <github@blinkov.ru>
2020-05-06 22:28:06 +03:00

60 KiB
Raw Blame History

machine_translated machine_translated_rev toc_priority toc_title
true 0f7ef7704d 33 SELECT

选择查询语法

SELECT 执行数据检索。

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

所有子句都是可选的除了紧接在SELECT之后的必需表达式列表。 以下子句的描述顺序几乎与查询执行传送器中的顺序相同。

如果查询省略 DISTINCT, GROUP BYORDER BY 条款和 INJOIN 子查询查询将被完全流处理使用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. 有关详细信息,请参阅部分 “Settings”. 可以使用外部排序(将临时表保存到磁盘)和外部聚合。 The system does not have "merge join".

WITH条款

本节提供对公共表表达式的支持 (CTE),有一些限制:

  1. 不支持递归查询
  2. 当在section中使用子查询时它的结果应该是只有一行的标量
  3. Expression的结果在子查询中不可用 WITH子句表达式的结果可以在SELECT子句中使用。

示例1使用常量表达式作为 “variable”

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

示例2从SELECT子句列表中逐出sum(bytes)表达式结果

WITH sum(bytes) as s
SELECT
    formatReadableSize(s),
    table
FROM system.parts
GROUP BY table
ORDER BY s

示例3使用标量子查询的结果

/* 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

示例4在子查询中重用表达式 作为子查询中表达式使用的当前限制的解决方法,您可以复制它。

WITH ['hello'] AS hello
SELECT
    hello,
    *
FROM
(
    WITH ['hello'] AS hello
    SELECT hello
)
┌─hello─────┬─hello─────┐
│ ['hello'] │ ['hello'] │
└───────────┴───────────┘

FROM条款

如果FROM子句被省略数据将从读取 system.one 桌子 该 system.one 表只包含一行此表满足与其他Dbms中找到的双表相同的目的

FROM 子句指定从中读取数据的源:

ARRAY JOIN 和常规 JOIN 也可以包括在内(见下文)。

而不是一个表,该 SELECT 子查询可以在括号中指定。 与标准SQL相比不需要在子查询后指定同义词。

若要执行查询,将从相应的表中提取查询中列出的所有列。 外部查询不需要的任何列都将从子查询中抛出。 如果查询未列出任何列(例如, SELECT count() FROM t),无论如何都会从表中提取一些列(最小的列是首选),以便计算行数。

最终修饰符

从表中选择数据时适用 MergeTree-发动机系列比其他 GraphiteMergeTree. 当 FINAL 如果指定ClickHouse会在返回结果之前完全合并数据从而执行给定表引擎合并期间发生的所有数据转换。

还支持:

使用的查询 FINAL 执行速度不如类似的查询那么快,因为:

  • 查询在单个线程中执行,并在查询执行期间合并数据。
  • 查询与 FINAL 除了读取查询中指定的列之外,还读取主键列。

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

示例子句

SAMPLE 子句允许近似查询处理。

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

近似查询处理在以下情况下可能很有用:

  • 当你有严格的时间requirements如<100ms但你不能证明额外的硬件资源来满足他们的成本。
  • 当您的原始数据不准确时,所以近似不会明显降低质量。
  • 业务需求的目标是近似结果(为了成本效益,或者为了向高级用户推销确切的结果)。

!!! note "注" 您只能使用采样中的表 MergeTree 家庭,并且只有在表创建过程中指定了采样表达式(请参阅 MergeTree引擎).

下面列出了数据采样的功能:

  • 数据采样是一种确定性机制。 同样的结果 SELECT .. SAMPLE 查询始终是相同的。
  • 对于不同的表,采样工作始终如一。 对于具有单个采样键的表,具有相同系数的采样总是选择相同的可能数据子集。 例如用户Id的示例采用来自不同表的所有可能的用户Id的相同子集的行。 这意味着您可以在子查询中使用示例 IN 条款 此外,您可以使用 JOIN 条款
  • 采样允许从磁盘读取更少的数据。 请注意,您必须正确指定采样键。 有关详细信息,请参阅 创建MergeTree表.

SAMPLE 子句支持以下语法:

SAMPLE Clause Syntax 产品描述
SAMPLE k 这里 k 是从0到1的数字。
查询执行于 k 数据的分数。 例如, SAMPLE 0.1 对10%的数据运行查询。 碌莽禄more拢more
SAMPLE n 这里 n 是足够大的整数。
该查询是在至少一个样本上执行的 n 行(但不超过这个)。 例如, SAMPLE 10000000 在至少10,000,000行上运行查询。 碌莽禄more拢more
SAMPLE k OFFSET m 这里 km 是从0到1的数字。
查询在以下示例上执行 k 数据的分数。 用于采样的数据由以下偏移 m 分数。 碌莽禄more拢more

SAMPLE K

这里 k 从0到1的数字支持小数和小数表示法。 例如, SAMPLE 1/2SAMPLE 0.5.

在一个 SAMPLE k 子句,样品是从 k 数据的分数。 示例如下所示:

SELECT
    Title,
    count() * 10 AS PageViews
FROM hits_distributed
SAMPLE 0.1
WHERE
    CounterID = 34
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000

在此示例中对0.1(10%)数据的样本执行查询。 聚合函数的值不会自动修正,因此要获得近似结果,值 count() 手动乘以10。

SAMPLE N

这里 n 是足够大的整数。 例如, SAMPLE 10000000.

在这种情况下,查询在至少一个样本上执行 n 行(但不超过这个)。 例如, SAMPLE 10000000 在至少10,000,000行上运行查询。

由于数据读取的最小单位是一个颗粒(其大小由 index_granularity 设置),是有意义的设置一个样品,其大小远大于颗粒。

使用时 SAMPLE n 子句,你不知道处理了哪些数据的相对百分比。 所以你不知道聚合函数应该乘以的系数。 使用 _sample_factor 虚拟列得到近似结果。

_sample_factor 列包含动态计算的相对系数。 当您执行以下操作时,将自动创建此列 创建 具有指定采样键的表。 的使用示例 _sample_factor 列如下所示。

让我们考虑表 visits,其中包含有关网站访问的统计信息。 第一个示例演示如何计算页面浏览量:

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

这里 km 是从0到1的数字。 示例如下所示。

示例1

SAMPLE 1/10

在此示例中,示例是所有数据的十分之一:

[++------------]

示例2

SAMPLE 1/10 OFFSET 1/2

这里从数据的后半部分取出10的样本。

[------++------]

ARRAY JOIN子句

允许执行 JOIN 具有数组或嵌套数据结构。 意图类似于 arrayJoin 功能,但其功能更广泛。

SELECT <expr_list>
FROM <left_subquery>
[LEFT] ARRAY JOIN <array>
[WHERE|PREWHERE <expr>]
...

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

运行时优化查询执行顺序 ARRAY JOIN. 虽然 ARRAY JOIN 必须始终之前指定 WHERE/PREWHERE 子句,它可以执行之前 WHERE/PREWHERE (如果结果是需要在这个子句),或完成后(以减少计算量)。 处理顺序由查询优化器控制。

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

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

下面的例子演示的用法 ARRAY JOINLEFT ARRAY JOIN 条款 让我们创建一个表 阵列 键入column并在其中插入值:

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加入"也适用于 嵌套数据结构. 示例:

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

JOIN子句

加入正常的数据 SQL JOIN 感觉

!!! info "注" 不相关的 ARRAY 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>) ...

可以指定表名,而不是 <left_subquery><right_subquery>. 这相当于 SELECT * FROM table 子查询,除了在特殊情况下,当表具有 加入我们 engine an array prepared for joining.

支持的类型 JOIN

  • INNER JOIN (或 JOIN)
  • LEFT JOIN (或 LEFT OUTER JOIN)
  • RIGHT JOIN (或 RIGHT OUTER JOIN)
  • FULL JOIN (或 FULL OUTER JOIN)
  • CROSS JOIN (或 , )

查看标准 SQL JOIN 描述。

多联接

执行查询时ClickHouse将多表联接重写为双表联接的序列。 例如如果有四个连接表ClickHouse连接第一个和第二个然后将结果与第三个表连接并在最后一步它连接第四个表。

如果查询包含 WHERE 子句ClickHouse尝试通过中间联接从此子句推下过滤器。 如果无法将筛选器应用于每个中间联接ClickHouse将在所有联接完成后应用筛选器。

我们建议 JOIN ONJOIN USING 用于创建查询的语法。 例如:

SELECT * FROM t1 JOIN t2 ON t1.a = t2.a JOIN t3 ON t1.a = t3.a

您可以使用逗号分隔的列表中的表 FROM 条款 例如:

SELECT * FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a

不要混合使用这些语法。

ClickHouse不直接支持使用逗号的语法所以我们不建议使用它们。 该算法尝试重写查询 CROSS JOININNER JOIN 子句,然后继续进行查询处理。 重写查询时ClickHouse会尝试优化性能和内存消耗。 默认情况下ClickHouse将逗号视为 INNER JOIN 子句和转换 INNER JOINCROSS JOIN 当算法不能保证 INNER JOIN 返回所需的数据。

严格

  • ALL — If the right table has several matching rows, ClickHouse creates a 笛卡尔积 从匹配的行。 这是标准 JOIN SQL中的行为。
  • ANY — If the right table has several matching rows, only the first one found is joined. If the right table has only one matching row, the results of queries with ANYALL 关键字是相同的。
  • ASOF — For joining sequences with a non-exact match. ASOF JOIN 用法描述如下。

ASOF加入使用

ASOF JOIN 当您需要连接没有完全匹配的记录时非常有用。

ASOF JOIN 必须具有有序序列列。 此列不能单独存在于表中,并且应该是其中一种数据类型: UInt32, UInt64, Float32, Float64, Date,和 DateTime.

语法 ASOF JOIN ... ON:

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 ... USING:

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

ASOF JOIN 用途 equi_columnX 对于加入平等和 asof_column 用于加入与最接近的比赛 table_1.asof_column >= table_2.asof_column 条件。 该 asof_column 列总是在最后一个 USING 条款

例如,请考虑下表:

     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
              ...                               ...

ASOF JOIN 可以从用户事件的时间戳 table_1 并找到一个事件 table_2 其中时间戳最接近事件的时间戳 table_1 对应于最接近的匹配条件。 如果可用,则相等的时间戳值是最接近的值。 在这里,该 user_id 列可用于连接相等和 ev_time 列可用于在最接近的匹配加入。 在我们的例子中, event_1_1 可以加入 event_2_1event_1_2 可以加入 event_2_3,但是 event_2_2 不能加入。

!!! note "注" ASOF 加入是 支持在 加入我们 表引擎。

若要设置默认严格性值请使用session configuration参数 join_default_strictness.

GLOBAL JOIN

当使用正常 JOIN,将查询发送到远程服务器。 为了创建正确的表,在每个子查询上运行子查询,并使用此表执行联接。 换句话说,在每个服务器上单独形成右表。

使用时 GLOBAL ... JOIN,首先请求者服务器运行一个子查询来计算正确的表。 此临时表将传递到每个远程服务器,并使用传输的临时数据对其运行查询。

使用时要小心 GLOBAL. 有关详细信息,请参阅部分 分布式子查询.

使用建议

当运行 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 两个子查询中必须具有相同的名称,并且其他列必须以不同的方式命名。 您可以使用别名更改子查询中的列名(此示例使用别名 hitsvisits).

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

正确的表子查询结果驻留在RAM中。 如果没有足够的内存,则无法运行 JOIN.

每次使用相同的查询运行 JOIN,子查询再次运行,因为结果未缓存。 为了避免这种情况,使用特殊的 加入我们 表引擎它是一个用于连接的准备好的数组总是在RAM中。

在某些情况下,使用效率更高 IN 而不是 JOIN. 在各种类型的 JOIN,最有效的是 ANY LEFT JOIN,然后 ANY INNER JOIN. 效率最低的是 ALL LEFT JOINALL INNER JOIN.

如果你需要一个 JOIN 对于连接维度表(这些是包含维度属性的相对较小的表,例如广告活动的名称), JOIN 由于每个查询都会重新访问正确的表,因此可能不太方便。 对于这种情况下,有一个 “external dictionaries” 您应该使用的功能 JOIN. 有关详细信息,请参阅部分 外部字典.

内存限制

ClickHouse使用 哈希联接 算法。 ClickHouse采取 <right_subquery> 并在RAM中为其创建哈希表。 如果需要限制联接操作内存消耗,请使用以下设置:

当任何这些限制达到ClickHouse作为 join_overflow_mode 设置指示。

处理空单元格或空单元格

在连接表时,可能会出现空单元格。 设置 join_use_nulls 定义ClickHouse如何填充这些单元格。

如果 JOIN 键是 可为空 字段,其中至少有一个键具有值的行 NULL 没有加入。

语法限制

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

  • 通过以所有列 * 仅在联接表时才可用,而不是子查询。
  • PREWHERE 条款不可用。

ON, WHERE,和 GROUP BY 条款:

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

WHERE条款

如果存在WHERE子句则必须包含具有UInt8类型的表达式。 这通常是一个带有比较和逻辑运算符的表达式。 此表达式将用于在所有其他转换之前过滤数据。

如果数据库表引擎支持索引,则根据使用索引的能力计算表达式。

PREWHERE条款

此条款与WHERE条款具有相同的含义。 区别在于从表中读取数据。 使用PREWHERE时首先只读取执行PREWHERE所需的列。 然后读取运行查询所需的其他列但只读取PREWHERE表达式为true的那些块。

如果查询中的少数列使用过滤条件但提供强大的数据过滤则使用PREWHERE是有意义的。 这减少了要读取的数据量。

例如对于提取大量列但仅对少数列进行过滤的查询编写PREWHERE非常有用。

PREWHERE仅由来自 *MergeTree 家人

查询可以同时指定PREWHERE和WHERE。 在这种情况下PREWHERE先于WHERE。

如果 optimize_move_to_prewhere 设置设置为1并省略PREWHERE系统使用启发式方法自动将部分表达式从哪里移动到哪里。

GROUP BY子句

这是面向列的DBMS最重要的部分之一。

如果存在GROUP BY子句则必须包含表达式列表。 每个表达式将在这里被称为 “key”. SELECT、HAVING和ORDER BY子句中的所有表达式都必须从键或聚合函数计算。 换句话说,从表中选择的每个列必须在键或聚合函数内使用。

如果查询仅包含聚合函数中的表列则可以省略GROUP BY子句并假定通过一组空键进行聚合。

示例:

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

但是与标准SQL相比如果表没有任何行根本没有任何行或者在使用WHERE to filter之后没有任何行则返回一个空结果而不是来自包含聚合函数初始值的行之

相对于MySQL并且符合标准SQL您无法获取不在键或聚合函数常量表达式除外中的某些列的某些值。 要解决此问题,您可以使用 any 聚合函数(获取第一个遇到的值)或 min/max.

示例:

SELECT
    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计算一组聚合函数值。

数组列不支持分组依据。

不能将常量指定为聚合函数的参数。 示例sum(1)。 相反,你可以摆脱常数。 示例: count().

空处理

对于GROUP BY子句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 BYy = NULL 总结 x,仿佛 NULL 是这个值。

如果你通过几个键 GROUP BY,结果会给你选择的所有组合,就好像 NULL 是一个特定的值。

使用总计修饰符

如果指定了WITH TOTALS修饰符则将计算另一行。 此行将具有包含默认值(零或空行)的关键列,以及包含跨所有行计算值的聚合函数列( “total” 值)。

这个额外的行以JSON*TabSeparated*和Pretty*格式输出,与其他行分开。 在其他格式中,此行不输出。

在JSON*格式中,此行作为单独的输出 totals 场。 在TabSeparated*格式中,该行位于主结果之后,前面有一个空行(在其他数据之后)。 在Pretty*格式中,该行在主结果之后作为单独的表输出。

WITH TOTALS 当有存在时,可以以不同的方式运行。 该行为取决于 totals_mode 设置。 默认情况下, totals_mode = 'before_having'. 在这种情况下, totals 是跨所有行计算,包括那些不通过具有和 max_rows_to_group_by.

其他替代方案仅包括通过具有在 totals并与设置不同的行为 max_rows_to_group_bygroup_by_overflow_mode = 'any'.

after_having_exclusive Don't include rows that didn't pass through max_rows_to_group_by. 换句话说, totals 将有少于或相同数量的行,因为它会 max_rows_to_group_by 被省略。

after_having_inclusive Include all the rows that didn't pass through max_rows_to_group_bytotals. 换句话说, totals 将有多个或相同数量的行,因为它会 max_rows_to_group_by 被省略。

after_having_auto Count the number of rows that passed through HAVING. If it is more than a certain amount (by default, 50%), include all the rows that didn't pass through max_rows_to_group_bytotals. 否则,不包括它们。

totals_auto_threshold By default, 0.5. The coefficient for after_having_auto.

如果 max_rows_to_group_bygroup_by_overflow_mode = 'any' 不使用,所有的变化 after_having 是相同的,你可以使用它们中的任何一个(例如, after_having_auto).

您可以在子查询中使用总计包括在JOIN子句中的子查询在这种情况下将合并各自的总计值

在外部存储器中分组

您可以启用将临时数据转储到磁盘以限制内存使用期间 GROUP BY. 该 max_bytes_before_external_group_by 设置确定倾销的阈值RAM消耗 GROUP BY 临时数据到文件系统。 如果设置为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 到10000000000max_memory_usage到20000000000。 当触发外部聚合如果至少有一个临时数据转储RAM的最大消耗仅略高于 max_bytes_before_external_group_by.

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

当合并数据刷新到磁盘时,以及当合并来自远程服务器的结果时, distributed_aggregation_memory_efficient 设置被启用,消耗高达 1/256 * the_number_of_threads 从RAM的总量。

当启用外部聚合时,如果有小于 max_bytes_before_external_group_by of data (i.e. data was not flushed), the query runs just as fast as without external aggregation. If any temporary data was flushed, the run time will be several times longer (approximately three times).

如果你有一个 ORDER BY 用一个 LIMITGROUP BY然后使用的RAM的量取决于数据的量 LIMIT,不是在整个表。 但如果 ORDER BY 没有 LIMIT,不要忘记启用外部排序 (max_bytes_before_external_sort).

限制条款

与查询 LIMIT n BY expressions 子句选择第一个 n 每个不同值的行 expressions. 的关键 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 每个不同组合的行 expressions. 如果 OFFSET 被指定,则对于每个数据块属于一个不同的组合 expressionsClickHouse跳过 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个引用 domain, 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

有条款

允许筛选GROUP BY之后收到的结果类似于WHERE子句。 WHERE和HAVING的不同之处在于WHERE是在聚合GROUP BY之前执行的而HAVING是在聚合之后执行的。 如果不执行聚合则不能使用HAVING。

按条款订购

ORDER BY子句包含一个表达式列表每个表达式都可以分配DESC或ASC排序方向。 如果未指定方向则假定ASC。 ASC按升序排序DESC按降序排序。 排序方向适用于单个表达式,而不适用于整个列表。 示例: ORDER BY Visits DESC, SearchPhrase

对于按字符串值排序,可以指定排序规则(比较)。 示例: ORDER BY SearchPhrase COLLATE 'tr' -对于按关键字升序排序使用土耳其字母不区分大小写假设字符串是UTF-8编码。 COLLATE可以按顺序独立地为每个表达式指定或不指定。 如果指定了ASC或DESC则在其后指定COLLATE。 使用COLLATE时排序始终不区分大小写。

我们只建议使用COLLATE对少量行进行最终排序因为使用COLLATE进行排序的效率低于正常按字节进行排序的效率。

对于排序表达式列表具有相同值的行,将以任意顺序输出,也可以是不确定的(每次都不同)。 如果省略ORDER BY子句则行的顺序也是未定义的并且可能也是不确定的。

NaNNULL 排序顺序:

  • 使用修饰符 NULLS FIRST — First NULL,然后 NaN,然后其他值。
  • 使用修饰符 NULLS LAST — First the values, then NaN,然后 NULL.
  • Default — The same as with the NULLS LAST 修饰符。

示例:

对于表

┌─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之外指定了足够小的限制则使用较少的RAM。 否则,所花费的内存量与用于排序的数据量成正比。 对于分布式查询处理如果省略GROUP BY则在远程服务器上部分完成排序并在请求者服务器上合并结果。 这意味着对于分布式排序,要排序的数据量可以大于单个服务器上的内存量。

如果没有足够的RAM则可以在外部存储器中执行排序在磁盘上创建临时文件。 使用设置 max_bytes_before_external_sort 为此目的。 如果将其设置为0默认值则禁用外部排序。 如果启用,则当要排序的数据量达到指定的字节数时,将对收集的数据进行排序并转储到临时文件中。 读取所有数据后,将合并所有已排序的文件并输出结果。 文件被写入配置中的/var/lib/clickhouse/tmp/目录(默认情况下,但您可以使用 tmp_path 参数来更改此设置)。

运行查询可能占用的内存比 max_bytes_before_external_sort. 因此,此设置的值必须大大小于 max_memory_usage. 例如如果您的服务器有128GB的RAM并且您需要运行单个查询请设置 max_memory_usage 到100GBmax_bytes_before_external_sort 至80GB。

外部排序的工作效率远远低于在RAM中进行排序。

SELECT子句

表达式 在指定 SELECT 子句是在上述子句中的所有操作完成后计算的。 这些表达式的工作方式就好像它们应用于结果中的单独行一样。 如果在表达式 SELECT 子句包含聚合函数然后ClickHouse处理过程中用作其参数的聚合函数和表达式 GROUP BY 聚合。

如果要在结果中包含所有列,请使用星号 (*)符号。 例如, SELECT * FROM ....

将结果中的某些列与 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') 返回两列: aaab. COLUMNS('c') 返回 bc 列。 该 + 运算符不能应用于3个参数因此ClickHouse引发一个带有相关消息的异常。

匹配的列 COLUMNS 表达式可以具有不同的数据类型。 如果 COLUMNS 不匹配任何列,并且是唯一的表达式 SELECTClickHouse抛出异常。

DISTINCT子句

如果指定了DISTINCT则在结果中所有完全匹配的行集中只有一行将保留。 结果将与在没有聚合函数的情况下在SELECT中指定的所有字段中指定GROUP BY一样。 但有几个区别,从组通过:

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

如果SELECT至少有一个数组列则不支持DISTINCT。

DISTINCT 适用于 NULL 就好像 NULL 是一个特定的值,并且 NULL=NULL. 换句话说,在 DISTINCT 结果,不同的组合 NULL 只发生一次。

ClickHouse支持使用 DISTINCTORDER BY 一个查询中不同列的子句。 该 DISTINCT 子句之前执行 ORDER BY 条款

示例表:

┌─a─┬─b─┐
│ 2 │ 1 │
│ 1 │ 2 │
│ 3 │ 3 │
│ 2 │ 4 │
└───┴───┘

DISTINCT可以与 NULL一起工作,就好像NULL仅是一个特殊的值一样,并且NULL=NULL。换而言之,在DISTINCT的结果中,与NULL不同的组合仅能出现一次。

┌─a─┐
│ 2 │
│ 1 │
│ 3 │
└───┘

如果我们改变排序方向 SELECT DISTINCT a FROM t1 ORDER BY b DESC,我们得到以下结果:

┌─a─┐
│ 3 │
│ 1 │
│ 2 │
└───┘

2, 4 分拣前被切割。

在编程查询时考虑这种实现特异性。

限制条款

LIMIT m 允许您选择第一个 m 结果中的行。

LIMIT n, m 允许您选择第一个 m 跳过第一个结果后的行 n 行。 该 LIMIT m OFFSET n 也支持语法。

nm 必须是非负整数。

如果没有 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 DISTINCT。 如果您需要UNION DISTINCT则可以从包含UNION ALL的子查询中编写SELECT DISTINCT。

作为UNION ALL部分的查询可以同时运行并且它们的结果可以混合在一起。

结果的结构(列的数量和类型)必须与查询匹配。 但列名可能不同。 在这种情况下,最终结果的列名将从第一个查询中获取。 对联合执行类型转换。 例如,如果合并的两个查询具有相同的字段与非-NullableNullable 从兼容类型的类型,由此产生的 UNION ALL 有一个 Nullable 类型字段。

作为UNION ALL部分的查询不能括在括号中。 ORDER BY和LIMIT应用于单独的查询而不是最终结果。 如果您需要将转换应用于最终结果则可以将所有带有UNION ALL的查询放在FROM子句的子查询中。

INTO OUTFILE条款

添加 INTO OUTFILE filename 子句其中filename是字符串文字用于将查询输出重定向到指定的文件。 与MySQL相比该文件是在客户端创建的。 如果具有相同文件名的文件已经存在,则查询将失败。 此功能在命令行客户端和clickhouse-local中可用通过HTTP接口发送的查询将失败

默认输出格式为TabSeparated与命令行客户端批处理模式相同

格式子句

指定 FORMAT format 获取任何指定格式的数据。 为了方便起见,您可以使用它或创建转储。 有关详细信息,请参阅部分 “Formats”. 如果省略FORMAT子句则使用默认格式这取决于用于访问数据库的设置和接口。 对于http接口和批处理模式下的命令行客户端默认格式为TabSeparated。 对于交互模式下的命令行客户端默认格式为PrettyCompact它具有吸引力和紧凑的表

使用命令行客户端时,数据以内部高效格式传递给客户端。 客户端独立解释查询的FORMAT子句并格式化数据本身从而减轻网络和服务器的负载

在运营商

IN, NOT IN, GLOBAL IN,和 GLOBAL NOT IN 运算符是单独复盖的,因为它们的功能相当丰富。

运算符的左侧是单列或元组。

例:

SELECT UserID IN (123, 456) FROM ...
SELECT (CounterID, UserID) IN ((34, 123), (101500, 456)) FROM ...

如果左侧是索引中的单列,而右侧是一组常量,则系统将使用索引处理查询。

Don't list too many values explicitly (i.e. millions). If a data set is large, put it in a temporary table (for example, see the section “External data for query processing”然后使用子查询。

运算符的右侧可以是一组常量表达式、一组带有常量表达式的元组如上面的示例所示或括号中的数据库表或SELECT子查询的名称。

如果运算符的右侧是表的名称(例如, UserID IN users),这相当于子查询 UserID IN (SELECT * FROM users). 使用与查询一起发送的外部数据时,请使用此选项。 例如查询可以与一组用户Id一起发送到 users 应过滤的临时表。

如果运算符的右侧是具有Set引擎的表名始终位于RAM中的准备好的数据集则不会为每个查询重新创建数据集。

子查询可以指定多个用于筛选元组的列。 示例:

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

IN运算符左侧和右侧的列应具有相同的类型。

IN运算符和子查询可能出现在查询的任何部分包括聚合函数和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 │
└────────────┴──────────┘

对于3月17日后的每一天计算3月17日访问该网站的用户所做的浏览量百分比。 IN子句中的子查询始终只在单个服务器上运行一次。 没有依赖子查询。

空处理

在处理中IN操作符总是假定 NULL 值的操作结果总是等于0,而不管NULL位于左侧还是右侧。NULL值不应该包含在任何数据集中,它们彼此不能够对应,并且不能够比较。

下面是一个例子 t_null 表:

┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 2 │    3 │
└───┴──────┘

运行查询 SELECT x FROM t_null WHERE y IN (NULL,3) 为您提供以下结果:

┌─x─┐
│ 2 │
└───┘

你可以看到,在其中的行 y = NULL 被抛出的查询结果。 这是因为ClickHouse无法决定是否 NULL 包含在 (NULL,3) 设置,返回 0 作为操作的结果,和 SELECT 从最终输出中排除此行。

SELECT y IN (NULL, 3)
FROM t_null
┌─in(y, tuple(NULL, 3))─┐
│                     0 │
│                     1 │
└───────────────────────┘

分布式子查询

带子查询的IN-s有两个选项类似于连接normal IN / JOINGLOBAL IN / GLOBAL JOIN. 它们在分布式查询处理的运行方式上有所不同。

!!! attention "注意" 请记住,下面描述的算法可能会有不同的工作方式取决于 设置 distributed_product_mode 设置。

当使用常规IN时查询被发送到远程服务器并且它们中的每个服务器都在运行子查询 INJOIN 条款

使用时 GLOBAL IN / GLOBAL JOINs,首先所有的子查询都运行 GLOBAL IN / GLOBAL JOINs,并将结果收集在临时表中。 然后将临时表发送到每个远程服务器,其中使用此临时数据运行查询。

对于非分布式查询,请使用常规 IN / JOIN.

在使用子查询时要小心 IN / JOIN 用于分布式查询处理的子句。

让我们来看看一些例子。 假设集群中的每个服务器都有一个正常的 local_table. 每个服务器还具有 distributed_table 表与 分布 类型,它查看群集中的所有服务器。

对于查询 distributed_table,查询将被发送到所有远程服务器,并使用以下命令在其上运行 local_table.

例如,查询

SELECT uniq(UserID) FROM distributed_table

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

SELECT uniq(UserID) FROM local_table

并且并行运行它们中的每一个,直到达到可以结合中间结果的阶段。 然后将中间结果返回给请求者服务器并在其上合并,并将最终结果发送给客户端。

现在让我们检查一个查询IN:

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子句中的数据集将在每台服务器上独立收集仅在每台服务器上本地存储的数据中收集。

如果您已经为此情况做好准备并且已经将数据分散到群集服务器上以便单个用户Id的数据完全驻留在单个服务器上则这将正常和最佳地工作。 在这种情况下,所有必要的数据将在每台服务器上本地提供。 否则,结果将是不准确的。 我们将查询的这种变体称为 “local IN”.

若要更正数据在群集服务器上随机传播时查询的工作方式,可以指定 distributed_table 在子查询中。 查询如下所示:

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)

子查询将开始在每个远程服务器上运行。 由于子查询使用分布式表,因此每个远程服务器上的子查询将重新发送到每个远程服务器

SELECT UserID FROM local_table WHERE CounterID = 34

例如如果您有100台服务器的集群则执行整个查询将需要10,000个基本请求这通常被认为是不可接受的。

在这种情况下应始终使用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更优化。 但是,请记住以下几点:

  1. 创建临时表时,数据不是唯一的。 要减少通过网络传输的数据量请在子查询中指定DISTINCT。 (你不需要为正常人做这个。)
  2. 临时表将被发送到所有远程服务器。 传输不考虑网络拓扑。 例如如果10个远程服务器驻留在与请求者服务器非常远程的数据中心中则数据将通过通道发送10次到远程数据中心。 使用GLOBAL IN时尽量避免使用大型数据集。
  3. 将数据传输到远程服务器时,无法配置网络带宽限制。 您可能会使网络过载。
  4. 尝试跨服务器分发数据以便您不需要定期使用GLOBAL IN。
  5. 如果您需要经常使用GLOBAL IN请规划ClickHouse集群的位置以便单个副本组驻留在不超过一个数据中心中并且它们之间具有快速网络以便可以完全在单个数据中心内处理查询。

这也是有意义的,在指定一个本地表 GLOBAL IN 子句,以防此本地表仅在请求者服务器上可用,并且您希望在远程服务器上使用来自它的数据。

极端值

除了结果之外,还可以获取结果列的最小值和最大值。 要做到这一点,设置 极端 设置为1。 最小值和最大值是针对数字类型、日期和带有时间的日期计算的。 对于其他列,默认值为输出。

An extra two rows are calculated the minimums and maximums, respectively. These extra two rows are output in JSON*, TabSeparated*,和 Pretty* 格式,与其他行分开。 它们不是其他格式的输出。

JSON* 格式时,极端值在一个单独的输出 extremes 场。 在 TabSeparated* 格式中,该行来的主要结果之后,和之后 totals 如果存在。 它前面有一个空行(在其他数据之后)。 在 Pretty* 格式中,该行被输出为一个单独的表之后的主结果,和之后 totals 如果存在。

极值计算之前的行 LIMIT,但之后 LIMIT BY. 但是,使用时 LIMIT offset, size,之前的行 offset 都包含在 extremes. 在流请求中,结果还可能包括少量通过的行 LIMIT.

GROUP BYORDER BY 子句不支持位置参数。 这与MySQL相矛盾但符合标准SQL。 例如, GROUP BY 1, 2 will be interpreted as grouping by constants (i.e. aggregation of all rows into one).

您可以使用同义词 (AS 别名)在查询的任何部分。

您可以在查询的任何部分而不是表达式中添加星号。 分析查询时,星号将展开为所有表列的列表(不包括 MATERIALIZEDALIAS 列)。 只有少数情况下使用星号是合理的:

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

在所有其他情况下我们不建议使用星号因为它只给你一个列DBMS的缺点而不是优点。 换句话说,不建议使用星号。

原始文章