跳至主要内容
跳至主要内容

语法

在本节中,我们将了解 ClickHouse 的 SQL 语法。ClickHouse 使用基于 SQL 的语法,但提供了许多扩展和优化。

查询解析

ClickHouse 中有两种类型的解析器

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

除了 INSERT 查询之外,完整 SQL 解析器在所有情况下均被使用,后者同时使用两种解析器。

让我们检查下面的查询

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 表达式

数据可以具有任何格式。当收到查询时,服务器在 RAM 中计算不超过 max_query_size 字节的请求(默认情况下为 1 MB),其余部分通过流解析。这是为了避免出现与大型 INSERT 查询相关的问题,这是在 ClickHouse 中插入数据的推荐方法。

在使用 INSERT 查询中的 Values 格式时,可能看起来数据解析方式与 SELECT 查询中的表达式解析方式相同,但事实并非如此。Values 格式的限制更多。

本节的其余部分涵盖完整的解析器。

注意

有关格式解析器的更多信息,请参阅 格式 部分。

空格

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

注释

ClickHouse 支持 SQL 样式和 C 样式注释

  • SQL 样式注释以 --#!# 开头,并持续到行尾。--#! 之后可以省略空格。
  • C 样式注释从 /* 跨越到 */,并且可以跨越多行。空格也不是必需的。

关键字

ClickHouse 中的关键字根据上下文可以是区分大小写不区分大小写的。

当关键字对应于

  • SQL 标准时,关键字不区分大小写。例如,SELECTselectSeLeCt 都是有效的。
  • 某些流行 DBMS(MySQL 或 Postgres)的实现。例如,DateTimedatetime 相同。
注意

您可以在 system.data_type_families 表中检查数据类型名称是否区分大小写。

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

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

例如,如果表 table_name 具有名为 "FROM" 的列,则以下查询有效

SELECT "FROM" FROM table_name

标识符

标识符是

标识符可以带引号或不带引号,但后者更受青睐。

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

有效的标识符无效的标识符
xyz_internalId_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 函数。
    • 如果该值以 0b0x/0X 为前缀,则该数字分别解析为二进制或十六进制。
    • 如果该值为负数且绝对值大于 263,则返回错误。
  • 如果失败,则该值接下来使用 strtod 函数解析为浮点数。
  • 否则,将返回错误。

字面值被转换为适合该值的最小类型。例如

  • 1 解析为 UInt8
  • 256 解析为 UInt16
重要提示

大于 64 位(UInt128Int128UInt256Int256)的整数值必须转换为更大的类型才能正确解析

-170141183460469231731687303715884105728::Int128
340282366920938463463374607431768211455::UInt128
-57896044618658097711785492504343953926634992332820282019728792003956564819968::Int256
115792089237316195423570985008687907853269984665640564039457584007913129639935::UInt256

这绕过了上述算法,并使用支持任意精度的例程解析整数。

否则,该字面量将被解析为浮点数,因此由于截断而导致精度损失。

有关更多信息,请参阅 数据类型

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

支持以下数字字面量

数字字面量示例
整数1, 10_000_000, 18446744073709551615, 01
小数0.1
指数表示法1e100-1e-100
浮点数123.456infnan
十六进制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 NULLIS NOT NULL 运算符以及相关的函数 isNullisNotNull 来检查 NULL

Heredoc

Heredoc 是一种定义字符串(通常是多行)的方法,同时保持原始格式。Heredoc 被定义为自定义字符串字面量,放置在两个 $ 符号之间。

例如

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

┌─'SHOW CREATE VIEW my_view'─┐
│ SHOW CREATE VIEW my_view   │
└────────────────────────────┘
注意
  • Heredoc 之间的值按原样处理。
提示
  • 您可以使用 heredoc 嵌入 SQL、HTML 或 XML 代码片段等。

定义和使用查询参数

查询参数允许您编写包含抽象占位符而不是具体标识符的通用查询。当执行带有查询参数的查询时,所有占位符都会被解析并替换为实际的查询参数值。

定义查询参数有两种方法

  • SET param_<name>=<value>
  • --param_<name>='<value>'

在使用第二种变体时,它作为命令行中的 clickhouse-client 的参数传递,其中

  • <name> 是查询参数的名称。
  • <value> 是其值。

查询参数可以在查询中使用 {<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};
注意

查询参数不是通用的文本替换,不能在任意 SQL 查询的任意位置使用。它们主要设计用于在 SELECT 语句中代替标识符或字面量。

函数

函数调用写起来像一个带有参数列表(可能为空)的标识符。与标准 SQL 相比,即使参数列表为空,也需要方括号。例如

now()

还有

一些聚合函数可以包含两个用方括号括起来的参数列表。例如

quantile (0.9)(x) 

这些聚合函数称为“参数化”函数,第一个列表中的参数称为“参数”。

注意

不带参数的聚合函数的语法与常规函数相同。

运算符

运算符在查询解析期间转换为其相应的函数,同时考虑其优先级和结合性。

例如,表达式

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 关键字具有另一个含义。请参阅函数的说明。
exprClickHouse 支持的任何表达式。SELECT column_name * 2 AS double FROM some_table
aliasexpr 的名称。别名应符合 标识符 语法。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)。这种替换导致了异常。

注意

您可以设置 prefer_column_name_to_alias1 来更改此默认行为。

星号

SELECT 查询中,星号可以替换表达式。有关更多信息,请参阅 SELECT 部分。

    © . This site is unofficial and not affiliated with ClickHouse, Inc.