语法
在本节中,我们将了解 ClickHouse 的 SQL 语法。ClickHouse 使用基于 SQL 的语法,但提供了许多扩展和优化。
查询解析
ClickHouse 中有两种类型的解析器
- 完整的 SQL 解析器(递归下降解析器)。
- 数据格式解析器(快速流解析器)。
除了 INSERT 查询之外,完整 SQL 解析器在所有情况下均被使用,后者同时使用两种解析器。
让我们检查下面的查询
如前所述,INSERT 查询同时使用两种解析器。INSERT INTO t VALUES 片段由完整解析器解析,而数据 (1, 'Hello, world'), (2, 'abc'), (3, 'def') 由数据格式解析器或快速流解析器解析。
启用完整解析器
您还可以使用 input_format_values_interpret_expressions 设置来为数据启用完整解析器。
当上述设置设置为 1 时,ClickHouse 首先尝试使用快速流解析器解析值。如果失败,ClickHouse 将尝试使用完整解析器解析数据,将其视为 SQL 表达式。
数据可以具有任何格式。当收到查询时,服务器在 RAM 中计算不超过 max_query_size 字节的请求(默认情况下为 1 MB),其余部分通过流解析。这是为了避免出现与大型 INSERT 查询相关的问题,这是在 ClickHouse 中插入数据的推荐方法。
在使用 INSERT 查询中的 Values 格式时,可能看起来数据解析方式与 SELECT 查询中的表达式解析方式相同,但事实并非如此。Values 格式的限制更多。
本节的其余部分涵盖完整的解析器。
有关格式解析器的更多信息,请参阅 格式 部分。
空格
- 在语法结构之间(包括查询的开头和结尾)可以存在任意数量的空格符号。
- 空格符号包括空格、制表符、换行符、CR 和换页符。
注释
ClickHouse 支持 SQL 样式和 C 样式注释
- SQL 样式注释以
--、#!或#开头,并持续到行尾。--和#!之后可以省略空格。 - C 样式注释从
/*跨越到*/,并且可以跨越多行。空格也不是必需的。
关键字
ClickHouse 中的关键字根据上下文可以是区分大小写或不区分大小写的。
当关键字对应于
- SQL 标准时,关键字不区分大小写。例如,
SELECT、select和SeLeCt都是有效的。 - 某些流行 DBMS(MySQL 或 Postgres)的实现。例如,
DateTime与datetime相同。
您可以在 system.data_type_families 表中检查数据类型名称是否区分大小写。
与标准 SQL 相比,所有其他关键字(包括函数名称)都是区分大小写的。
此外,关键字不是保留的。它们仅在相应上下文中才被视为关键字。如果您使用与关键字同名的 标识符,请将其用双引号或反引号括起来。
例如,如果表 table_name 具有名为 "FROM" 的列,则以下查询有效
标识符
标识符是
标识符可以带引号或不带引号,但后者更受青睐。
不带引号的标识符必须匹配正则表达式 ^[a-zA-Z_][0-9a-zA-Z_]*$,并且不能等于 关键字。有关有效和无效标识符的示例,请参见下表
| 有效的标识符 | 无效的标识符 |
|---|---|
xyz、_internal、Id_with_underscores_123_ | 1x、[email protected]、äußerst_schön |
如果您想使用与关键字相同的标识符,或者想在标识符中使用其他符号,请使用双引号或反引号对其进行引用,例如 "id"、`id`。
应用于带引号标识符的转义规则也适用于字符串字面量。有关更多详细信息,请参见 字符串。
字面量
在 ClickHouse 中,字面量是在查询中直接表示的值。换句话说,它是一个在查询执行期间不会改变的固定值。
字面量可以是
我们将在下面的章节中更详细地介绍这些内容。
字符串
字符串字面量必须用单引号括起来。不支持双引号。
转义通过以下方式工作
- 使用前导单引号,其中单引号字符
'(仅此字符)可以转义为'',或 - 使用以下支持的转义序列的前导反斜杠,如以下表格所示。
如果反斜杠后跟的字符不是以下列出的字符,则反斜杠将失去其特殊含义,即按字面意义解释。
| 支持的转义 | 描述 |
|---|---|
\xHH | 后跟任意数量的十六进制数字 (H) 的 8 位字符规范。 |
\N | 保留,不执行任何操作(例如 SELECT 'a\Nb' 返回 ab) |
\a | 警报 |
\b | 退格 |
\e | 转义字符 |
\f | 换页 |
\n | 换行符 |
\r | 回车符 |
\t | 水平制表符 |
\v | 垂直制表符 |
\0 | 空字符 |
\\ | 反斜杠 |
\'(或 '') | 单引号 |
\" | 双引号 |
` | 反引号 |
\/ | 正斜杠 |
\= | 等号 |
| ASCII 控制字符 (c <= 31)。 |
在字符串字面量中,您需要使用转义码 \'(或:'')和 \\ 转义至少 ' 和 \。
数字
数字字面量按如下方式解析
- 如果字面量以负号
-为前缀,则跳过该标记,并在解析后否定结果。 - 数字字面量首先被解析为 64 位无符号整数,使用 strtoull 函数。
- 如果该值以
0b或0x/0X为前缀,则该数字分别解析为二进制或十六进制。 - 如果该值为负数且绝对值大于 263,则返回错误。
- 如果该值以
- 如果失败,则该值接下来使用 strtod 函数解析为浮点数。
- 否则,将返回错误。
字面值被转换为适合该值的最小类型。例如
1解析为UInt8256解析为UInt16。
大于 64 位(UInt128、Int128、UInt256、Int256)的整数值必须转换为更大的类型才能正确解析
这绕过了上述算法,并使用支持任意精度的例程解析整数。
否则,该字面量将被解析为浮点数,因此由于截断而导致精度损失。
有关更多信息,请参阅 数据类型。
数字字面量内的下划线 _ 被忽略,可用于提高可读性。
支持以下数字字面量
| 数字字面量 | 示例 |
|---|---|
| 整数 | 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] 构造。元组使用圆括号 (1, 'Hello, world!', 2) 构造。从技术上讲,这些不是字面量,而是具有数组创建运算符和元组创建运算符的表达式。数组必须至少包含一个项目,元组必须至少包含两个项目。
当元组出现在 SELECT 查询的 IN 子句中时,存在一个单独的情况。查询结果可以包含元组,但元组不能保存到数据库中(除非使用 Memory 引擎的表)。
NULL
NULL 用于表示缺失的值。要在表字段中存储 NULL,它必须是 可为空 类型。
关于 NULL,应注意以下几点
- 根据数据格式(输入或输出),
NULL可能具有不同的表示形式。有关更多信息,请参阅 数据格式。 NULL处理具有细微差别。例如,如果比较操作的至少一个参数是NULL,则该操作的结果也是NULL。乘法、加法和其他操作也是如此。我们建议阅读每个操作的文档。- 在查询中,您可以使用
IS NULL和IS NOT NULL运算符以及相关的函数isNull和isNotNull来检查NULL。
Heredoc
Heredoc 是一种定义字符串(通常是多行)的方法,同时保持原始格式。Heredoc 被定义为自定义字符串字面量,放置在两个 $ 符号之间。
例如
- Heredoc 之间的值按原样处理。
- 您可以使用 heredoc 嵌入 SQL、HTML 或 XML 代码片段等。
定义和使用查询参数
查询参数允许您编写包含抽象占位符而不是具体标识符的通用查询。当执行带有查询参数的查询时,所有占位符都会被解析并替换为实际的查询参数值。
定义查询参数有两种方法
SET param_<name>=<value>--param_<name>='<value>'
在使用第二种变体时,它作为命令行中的 clickhouse-client 的参数传递,其中
<name>是查询参数的名称。<value>是其值。
查询参数可以在查询中使用 {<name>: <datatype>} 引用,其中 <name> 是查询参数名称,<datatype> 是它被转换为的数据类型。
使用 SET 命令的示例
例如,以下 SQL 定义了名为 a、b、c 和 d 的参数 - 每个参数具有不同的数据类型
使用 clickhouse-client 的示例
如果您正在使用 clickhouse-client,则参数指定为 --param_name=value。例如,以下参数的名称为 message,它被检索为 String
如果查询参数表示数据库、表、函数或其他标识符的名称,请使用 Identifier 作为其类型。例如,以下查询返回名为 uk_price_paid 的表中的行
查询参数不是通用的文本替换,不能在任意 SQL 查询的任意位置使用。它们主要设计用于在 SELECT 语句中代替标识符或字面量。
函数
函数调用写起来像一个带有参数列表(可能为空)的标识符。与标准 SQL 相比,即使参数列表为空,也需要方括号。例如
还有
一些聚合函数可以包含两个用方括号括起来的参数列表。例如
这些聚合函数称为“参数化”函数,第一个列表中的参数称为“参数”。
不带参数的聚合函数的语法与常规函数相同。
运算符
运算符在查询解析期间转换为其相应的函数,同时考虑其优先级和结合性。
例如,表达式
转换为
数据类型和数据库表引擎
在 CREATE 查询中,数据类型和表引擎的编写方式与标识符或函数相同。换句话说,它们可能包含或不包含方括号中的参数列表。
有关更多信息,请参阅以下部分
表达式
表达式可以是以下任何一种
- 一个函数
- 一个标识符
- 一个字面量
- 运算符的应用
- 带括号的表达式
- 一个子查询
- 一个星号
它还可以包含一个 别名。
表达式列表是一个或多个由逗号分隔的表达式。函数和运算符反过来可以具有表达式作为参数。
常量表达式是在查询分析期间(即在执行之前)已知结果的表达式。例如,字面量上的表达式是常量表达式。
表达式别名
别名是查询中 表达式 的用户定义名称。
上述语法的各个部分如下所述。
| 语法部分 | 描述 | 示例 | 注意事项 |
|---|---|---|---|
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 的名称。别名应符合 标识符 语法。 | SELECT "table t".column_name FROM table_name AS "table t". |
使用说明
- 别名对于查询或子查询是全局的,您可以在查询的任何部分为任何表达式定义别名。例如
- 别名在子查询和子查询之间不可见。例如,在执行以下查询时,ClickHouse 会生成异常
Unknown identifier: num
- 如果在子查询的
SELECT子句中为结果列定义了别名,则这些列在外部查询中可见。例如
- 小心与列或表名相同的别名。让我们考虑以下示例
在前面的示例中,我们声明了带有列 b 的表 t。然后,在选择数据时,我们定义了别名 sum(b) AS b。由于别名是全局的,ClickHouse 将表达式 argMax(a, b) 中的字面量 b 替换为表达式 sum(b)。这种替换导致了异常。
您可以设置 prefer_column_name_to_alias 为 1 来更改此默认行为。
星号
在 SELECT 查询中,星号可以替换表达式。有关更多信息,请参阅 SELECT 部分。