SELECT 查询后都可以接一串管道运算符。每个运算符均以 |> 标记开头,以上一个查询的结果作为输入,并在此基础上再进行一次转换。每个运算符内部均使用常规的 ClickHouse 语法。
管道运算符是一种语法扩展:每个运算符都会将其前面的查询封装为子查询,因此生成的 AST 与使用嵌套子查询编写的等效查询的 AST 相同。上述查询等效于:
FROM 查询
FROM 子句开头,且此类查询中的 SELECT 子句为可选项;省略时,其效果等同于写入 SELECT *:
AS 关键字,与普通 SELECT 查询的 FROM 子句相同:FROM orders o WHERE o.amount > 100。唯一的例外是将裸词 select 用作别名:它位于表名之后时,会开始显式的 SELECT 子句,而不会被视为别名。名为 select 的表不受影响,仍可使用自己的别名:FROM select s WHERE s.id = 1。
如果最后一个表的样本偏移量也可能被解析为查询级别的 OFFSET,则不能省略 SELECT 子句:在 FROM t SAMPLE 1/10 OFFSET 5 中,OFFSET 属于 SAMPLE;而在 FROM t SAMPLE 1/10 SELECT * OFFSET 5 中,它是查询级别的 OFFSET。需要显式指定 SELECT 以消除两者的歧义。如果查询后面紧接的子句不能位于查询级别 OFFSET 之前,则不存在歧义,SELECT 子句仍可照常省略:FROM t SAMPLE 1/10 OFFSET 5 WHERE x > 0、FROM t SAMPLE 1/10 OFFSET 5 JOIN dim USING (id)。
运算符
WHERE
|> WHERE condition 用于过滤输入行。在聚合之后使用时,其作用类似于 HAVING:
SELECT
|> SELECT [DISTINCT] expr1 [AS alias1], ... 仅将列出的表达式保留为输出列:
SELECT 子句的尾随逗号相同——其后可以是查询结束,也可以是下一个 |> 运算符:FROM orders |> SELECT customer, amount, |> LIMIT 1。EXTEND 和 AGGREGATE 运算符也同样如此。
EXTEND
|> EXTEND expr1 [AS alias1], ... 会将列出的表达式添加到输入列中;等同于 SELECT *, expr1 AS alias1, ...:
SET
|> SET column1 = expr1, ... 用于替换所列列的值;它等同于 SELECT * REPLACE (expr1 AS column1, ...):
DROP
|> DROP column1, ... 用于删除列出的列;等同于 SELECT * EXCEPT (column1, ...):
AS
|> AS alias 为下一个运算符的输入指定别名,以便在该运算符中引用,主要用于 JOIN:
AGGREGATE
|> AGGREGATE agg1 [AS alias1], ... [GROUP BY expr1 [AS alias1], ...] 对输入行进行聚合。输出列依次为分组列和聚合列。不使用 GROUP BY 时,整个输入将聚合为单行:
DISTINCT
|> DISTINCT 会去除重复行;等同于 SELECT DISTINCT *。
ORDER BY
|> ORDER BY expr1 [ASC/DESC], ... 用于对输入行排序。支持完整的 ORDER BY 子句语法,包括 ORDER BY ALL、WITH FILL 和 INTERPOLATE:
LIMIT 和 OFFSET
|> LIMIT length [OFFSET offset] 和 |> OFFSET offset 用于限制返回的行数:
JOIN 和 ARRAY JOIN
|> [GLOBAL] [ANY/ALL/ASOF/SEMI/ANTI] [INNER/LEFT/RIGHT/FULL/CROSS] JOIN table [ON expr | USING (columns)] 会将输入与另一个表、子查询或表函数进行连接。支持所有类型的 JOIN 和 ARRAY JOIN,单个运算符中可包含多个连接,类似于 FROM 子句:
ON 条件中) 。后续运算符看到的是 JOIN 结果中合并后的列,就像在 SELECT * 之后一样。
也支持使用逗号形式的 CROSS JOIN,运算符的输入位于左侧:FROM customers |> AS c |> , orders。与其他 JOIN 一样,启用 joined_subquery_requires_alias 设置时,输入必须具有别名 (默认启用) 。
与普通查询的 FROM 子句一样,ARRAY JOIN 后不能紧接逗号 (CROSS) JOIN:ARRAY JOIN 后的逗号始终属于其表达式列表。
UNION、INTERSECT 和 EXCEPT
|> UNION [ALL/DISTINCT] (query1) [, (query2), ...]、|> INTERSECT [ALL/DISTINCT] ... 和 |> EXCEPT [ALL/DISTINCT] ... 将输入数据与其他查询的结果合并:
注意事项
- 查询的
WITH子句在所有后续管道运算符中始终可见,标量别名和 CTE 均是如此:WITH 10 AS threshold FROM t |> WHERE x < threshold。 - 在
INSERT ... SELECT中,写在INSERT前的WITH子句会附加到最外层生成的SELECT,并在解析时通过enable_global_with_statement设置 (默认启用) 传递到内部管道阶段,方式与传递到手写的嵌套子查询相同。若禁用该设置,作用域为INSERT的WITH中定义的别名和 CTE 在管道阶段内将不可见,就像在手写子查询内不可见一样。 - 与任何
SELECT查询一样,由管道运算符生成的查询可以以SETTINGS子句结尾,该子句会附加到生成的查询:FROM t |> LIMIT 1 SETTINGS max_threads = 1等同于SELECT * FROM (SELECT * FROM t) LIMIT 1 SETTINGS max_threads = 1。这同样适用于没有单独处理查询设置环节的场景,例如子查询、CREATE VIEW或view表函数中。位于链中间的SETTINGS子句会保留在所在阶段,而该阶段将成为下一个运算符的子查询。对于带括号操作数的集合操作,不接受其后的SETTINGS;等效的子查询形式在该位置同样不能包含SETTINGS子句。 - 第一个管道运算符之前的查询中的
SETTINGS子句会保留在该查询上,该查询会成为生成的包装查询的子查询。常规设置仍然有效,因为解析子查询时会应用其设置。唯一的例外是用于选择查询分析器的一对设置:enable_analyzer及其别名allow_experimental_analyzer。不允许在子查询中更改它们,因此SELECT number FROM numbers(1) SETTINGS enable_analyzer = 0 |> LIMIT 1会引发INCORRECT_QUERY,与等效的手写查询SELECT * FROM (SELECT number FROM numbers(1) SETTINGS enable_analyzer = 0) LIMIT 1完全相同。请将这两个设置写在最后一个管道运算符之后,或在查询外部传入。 - 管道运算符会绑定到其前的整个查询,包括集合操作:在
SELECT 1 UNION ALL SELECT 2 |> AGGREGATE count()中,聚合应用于UNION ALL的结果。若要在管道运算符后通过UNION继续查询,请使用|> UNION运算符或括号。 - 管道运算符可用于任何需要
SELECT查询的地方:子查询、INSERT ... SELECT(包括INSERT INTO t FROM src |> ...形式) 、CREATE VIEW、view表函数等。 - 不提供单独的就地列重命名运算符;请使用
|> SELECT * EXCEPT (old_name), old_name AS new_name,或使用SET和DROP运算符。