Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

语法

本节将介绍 ClickHouse 的 SQL 语法。 ClickHouse 使用基于 SQL 的语法,同时提供了多种扩展和优化。

查询解析

ClickHouse 中有两种解析器:

  • 完整的 SQL 解析器 (递归下降解析器) 。
  • 数据格式解析器 (快速流式解析器) 。

完整的 SQL 解析器用于除 INSERT 查询之外的所有场景,而 INSERT 查询会同时使用这两种解析器。

让我们来看下面这个查询:

INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def')

如前所述,INSERT 查询会同时用到这两种解析器。 INSERT INTO t VALUES 这一片段由完整解析器解析, 而数据 (1, 'Hello, world'), (2, 'abc'), (3, 'def') 则由数据格式解析器 (即快速流式解析器) 解析。

启用完整解析器

你也可以通过 input_format_values_interpret_expressions 设置, 为数据启用完整解析器。

当上述设置为 1 时, ClickHouse 会先尝试用快速流式解析器解析这些值。 如果失败,ClickHouse 会再尝试使用完整解析器解析数据,并将其视为 SQL 表达式

数据可以采用任意格式。 收到查询时,server 只会将请求中不超过 max_query_size 字节的内容放入 RAM 中进行计算 (默认为 1 MB) ,其余部分则以流式方式解析。 这样可以避免大型 INSERT 查询带来的问题,而这也是向 ClickHouse 插入数据的推荐方式。

INSERT 查询中使用 Values 格式时, 看起来数据的解析方式似乎与 SELECT 查询中的表达式相同,但实际上并非如此。 Values 格式的能力要受限得多。

本节其余内容将介绍完整解析器。

空格

  • 语法结构之间 (包括查询的开头和结尾) 可以有任意数量的空白字符。
  • 空白字符包括空格、制表符、换行符、CR 和换页符。

注释

ClickHouse 同时支持 SQL 风格注释和 C 风格注释:

  • SQL 风格注释以 --#!# 开头,并一直延续到行尾。--#! 后的空格可以省略。
  • C 风格注释:
    • // (或超过 2 个 / 字符) 后跟文本,并持续到行尾。/ 后不要求有空格。
    • 多行注释可以从 /* 一直延续到 */。同样不要求有空格。
    • C 风格注释可以嵌套。

例如:

/*
 * 计算两个日期之间的天数。
 * /* 如果任一参数为 NULL,则返回 NULL */
 */
SELECT
    dateDiff('day', toDate('2024-01-01'), toDate('2024-12-31')) AS days_in_year, -- 365
    dateDiff('day', toDate('2020-01-01'), today()) AS days_since  #! since 2020
    ///////////////////////////////////////////////////////////////////
    # TODO: 添加小时/分钟版本

关键字

ClickHouse 中的关键字是否 区分大小写不区分大小写,取决于具体上下文。

在以下情况下,关键字是 不区分大小写 的:

  • SQL 标准。例如,SELECTselectSeLeCt 都是有效的。
  • 某些流行 DBMS (MySQL 或 Postgres) 中的实现。例如,DateTimedatetime 等同。

与标准 SQL 不同,所有其他关键字 (包括函数名) 都是 区分大小写 的。

此外,关键字不是保留字。 它们仅在相应的上下文中才被视为关键字。 如果你使用与关键字同名的标识符,请将其括在双引号或反引号中。

例如,如果表 table_name 中有一列名为 "FROM",则以下查询是有效的:

SELECT "FROM" FROM table_name

标识符

标识符包括:

标识符可以带引号,也可以不带引号,不过更推荐后者。

不带引号的标识符必须匹配正则表达式 ^[a-zA-Z_][0-9a-zA-Z_]*$,且不能与关键字相同。 有效和无效标识符的示例见下表:

有效标识符 无效标识符
xyz, _internal, Id_with_underscores_123_ 1x, tom@gmail.com, äußerst_schön

如果你想使用与关键字相同的标识符,或者想在标识符中使用其他符号,请用双引号或反引号将其括起来,例如 "id"`id`

字面量

在 ClickHouse 中,字面量是指在查询中直接写出的值。 换句话说,它是在查询执行过程中不会改变的固定值。

字面量可以是:

下面各节将更详细地介绍这些类型。

String

String 字面量必须用单引号括起来。不支持双引号。

可以通过以下任一方式进行转义:

  • 使用前置单引号,此时单引号字符 ' (且仅限此字符) 可转义为 '';或者
  • 使用前置反斜杠,并配合下表中列出的受支持转义序列。
Supported Escape Description
\xHH 8 位字符表示法,后跟任意数量的十六进制数字 (H) 。
\N 保留,不执行任何操作 (例如 SELECT 'a\Nb' 返回 ab)
\a 响铃
\b 退格
\e 转义字符
\f 换页
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\0 空字符
\\ 反斜杠
\' (or '') 单引号
\" 双引号
` 反引号
\/ 正斜杠
\= 等号
ASCII control characters (c <= 31).

数值

数值字面量按以下方式解析:

  • 如果字面量以前导减号 - 开头,则会跳过该标记,并在解析完成后对结果取负。
  • 数值字面量首先会使用 strtoull 函数解析为 64 位无符号整数。
    • 如果值带有前缀 0b0x/0X,则分别按二进制或十六进制解析。
    • 如果值为负数且其绝对值大于 263,则返回错误。
  • 如果失败,则接着使用 strtod 函数将该值解析为浮点数。
  • 否则,返回错误。

字面量值会被转换为能够容纳该值的最小类型。 例如:

  • 1 会被解析为 UInt8
  • 256 会被解析为 UInt16

更多信息,请参见 数据类型

数值字面量中的下划线 _ 会被忽略,可用于提高可读性。

支持以下数值字面量:

数值字面量 示例
整数 1, 10_000_000, 18446744073709551615, 01
小数 0.1
科学计数法 1e100, -1e-100
浮点数 123.456, inf, nan
十六进制 0xc0fe
兼容 SQL 标准的十六进制字符串 x'c0fe'
二进制 0b1101
兼容 SQL 标准的二进制字符串 b'1101'

复合字面量

数组使用 [] 构造,例如 [1, 2, 3]。Tuple 使用 () 构造,例如 (1, 'Hello, world!', 2)。 严格来说,这些并不是字面量,而是分别使用数组创建运算符和 Tuple 创建运算符的表达式。 数组至少必须包含一个元素,而 Tuple 至少必须包含两个元素。

NULL

NULL 用于表示值缺失。 要在表字段中存储 NULL,该字段必须为 Nullable 类型。

Heredoc

heredoc 是一种定义字符串 (通常是多行字符串) 的方法,同时保留原始格式。 heredoc 是一种自定义字符串字面量,位于两个 $ 符号之间。

例如:

SELECT $heredoc$SHOW CREATE VIEW my_view$heredoc$;

定义和使用查询参数

查询参数可让你编写通用查询,在查询中使用抽象占位符,而不是具体标识符。 执行带有查询参数的查询时, 所有占位符都会被解析并替换为实际的查询参数值。

查询参数可以通过以下几种方式定义:

  • SET param_<name>=<value> — 在查询中使用 SET 命令。
  • --param_<name>='<value>' — 作为命令行中 clickhouse-client 的参数。
  • param_<name>=<value> — 作为 HTTP 接口的 URL 查询字符串参数。

可以在查询中使用 {<name>: <datatype>} 引用查询参数,其中 <name> 是查询参数名称,<datatype> 是要转换成的数据类型。

使用 SET 命令的示例

例如,下面的 SQL 定义了名为 abcd 的参数,每个参数的数据类型都不同:

SET param_a = 13;
SET param_b = 'str';
SET param_c = '2022-08-04 18:30:53';
SET param_d = {'10': [11, 12], '13': [14, 15]};

SELECT
   {a: UInt32},
   {b: String},
   {c: DateTime},
   {d: Map(String, Array(UInt8))};

13    str    2022-08-04 18:30:53    {'10':[11,12],'13':[14,15]}
使用 clickhouse-client 的示例

如果你使用 clickhouse-client,参数通过 --param_name=value 指定。例如,下面这个参数的名称为 message,并以 String 类型读取:

clickhouse-client --param_message='hello' --query="SELECT {message: String}"

hello

如果查询参数表示数据库、表、函数或其他标识符的名称,请使用 Identifier 作为其类型。例如,下面的查询会返回名为 uk_price_paid 的表中的行:

SET param_mytablename = "uk_price_paid";
SELECT * FROM {mytablename:Identifier};
使用 HTTP 接口的示例

查询参数可以通过带有 param_ 前缀的 URL 查询字符串参数传递。例如:

curl -s "http://localhost:8123/?param_message=hello" --data-binary "SELECT {message: String}"

hello
使用 Web UI 的示例

内置的 Web UI (play.html) 会自动检测查询中的 {name:Type} 参数占位符,并为每个参数显示带标签的输入框。参数值会包含在 HTTP 请求中,也会保存在页面 URL 中,以便添加书签和共享。

函数

函数调用的写法类似于一个标识符,后面跟着一组放在 () 中的参数列表 (可以为空) 。 与标准 SQL 不同,即使参数列表为空,也必须使用括号。 例如:

now()

还有:

某些聚合函数的括号内可以包含两组参数列表。例如:

quantile (0.9)(x) 

这些聚合函数被称为"参数化"函数, 而第一组列表中的参数被称为"parameters"。

运算符

在解析查询时,运算符会根据其优先级和结合性转换为相应的函数。

例如,表达式

1 + 2 * 3 + 4

转换为

plus(plus(1, multiply(2, 3)), 4)`

数据类型和数据库表引擎

CREATE 查询中的数据类型和表引擎,写法与标识符或函数相同。 也就是说,它们可以带括号中的参数列表,也可以不带。

更多信息,请参见以下章节:

表达式

表达式可以是以下任意一种:

  • 函数
  • 标识符
  • 字面量
  • 运算符应用
  • 括号中的表达式
  • 子查询
  • 星号

它也可以包含别名

表达式列表由一个或多个用逗号分隔的表达式组成。 函数和运算符也可以接受表达式作为参数。

常量表达式是指其结果在查询分析期间 (即执行之前) 即可确定的表达式。 例如,仅由字面量构成的表达式就是常量表达式。

表达式别名

别名是查询中某个表达式的用户自定义名称。

expr AS alias

上述语法各部分的说明如下。

语法组成部分 描述 示例 说明
AS 用于定义别名的关键字。你可以在 SELECT 子句中不使用 AS 关键字,直接为表名或列名指定别名。 SELECT table_name_alias.column_name FROM table_name table_name_alias. CAST 函数中,AS 关键字还有另一种含义。请参见该函数的说明。
expr ClickHouse 支持的任意 表达式。 SELECT column_name * 2 AS double FROM some_table
alias expr 的名称。别名应符合 identifiers 的语法。 SELECT "table t".column_name FROM table_name AS "table t".

使用说明

  • 别名在查询或子查询中是全局生效的,你可以在查询的任何部分为任何表达式定义别名。例如:
SELECT (1 AS n) + 2, n`.
  • 别名在子查询内部以及子查询之间都不可见。例如,执行以下查询时,ClickHouse 会抛出异常 Unknown identifier: num
`SELECT (SELECT sum(b.a) + num FROM b) - a.a AS num FROM a`
  • 如果在子查询的 SELECT 子句中为结果列定义了别名,则这些列在外层查询中也是可见的。例如:
SELECT n + m FROM (SELECT 1 AS n, 2 AS m)`.
  • 请注意避免使用与列名或表名相同的别名。以下是一个示例:
CREATE TABLE t
(
    a Int,
    b Int
)
ENGINE = TinyLog();

SELECT
    argMax(a, b),
    sum(b) AS b
FROM t;

Received exception from server (version 18.14.17):
Code: 184. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: Aggregate function sum(b) is found inside another aggregate function in query.

在前面的示例中,我们声明了包含列 b 的表 t。 然后,在查询数据时,我们定义了别名 sum(b) AS b。 由于别名是全局生效的, ClickHouse 将表达式 argMax(a, b) 中的字面量 b 替换成了表达式 sum(b)。 这种替换导致了异常。

星号

SELECT 查询中,星号可用于替代表达式。 更多信息,请参见 SELECT 一节。

Navigation